Skip to content

Commit

Permalink
chore: add test support for smart sessions
Browse files Browse the repository at this point in the history
  • Loading branch information
joepegler committed Sep 2, 2024
1 parent 678a5cc commit f657548
Show file tree
Hide file tree
Showing 14 changed files with 519 additions and 119 deletions.
Binary file modified bun.lockb
Binary file not shown.
2 changes: 1 addition & 1 deletion scripts/fetch:deployment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ export const getDeployments = async () => {
}

// Write ABI index file...
const abiIndexContent = `export * from "./EntryPointABI"\n${coreFiles
const abiIndexContent = `export * from "./UniActionPolicyAbi"\nexport * from "./EntryPointABI"\n${coreFiles
.map((file) => `export * from "./${file}Abi"`)
.join("\n")}`

Expand Down
58 changes: 58 additions & 0 deletions src/__contracts/abi/UniActionPolicyAbi.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
export const UniActionPolicyAbi = [
{
components: [
{
name: "valueLimitPerUse",
type: "uint256"
},
{
components: [
{
name: "length",
type: "uint256"
},
{
components: [
{
name: "condition",
type: "uint8"
},
{
name: "offset",
type: "uint64"
},
{
name: "isLimited",
type: "bool"
},
{
name: "ref",
type: "bytes32"
},
{
components: [
{
name: "limit",
type: "uint256"
},
{
name: "used",
type: "uint256"
}
],
name: "usage",
type: "tuple"
}
],
name: "rules",
type: "tuple[]"
}
],
name: "paramRules",
type: "tuple"
}
],
name: "ActionConfig",
type: "tuple"
}
] as const
1 change: 1 addition & 0 deletions src/__contracts/abi/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from "./UniActionPolicyAbi"
export * from "./EntryPointABI"
export * from "./NexusAbi"
export * from "./K1ValidatorAbi"
Expand Down
4 changes: 3 additions & 1 deletion src/__contracts/addresses.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
// The contents of this folder is auto-generated. Please do not edit as your changes are likely to be overwritten

import type { Hex } from "viem"
import { TEST_CONTRACTS } from "../../tests/src/callDatas"

Check failure on line 4 in src/__contracts/addresses.ts

View workflow job for this annotation

GitHub Actions / size report

File '/home/runner/work/biconomy-client-sdk/biconomy-client-sdk/tests/src/callDatas.ts' is not under 'rootDir' '/home/runner/work/biconomy-client-sdk/biconomy-client-sdk/src'. 'rootDir' is expected to contain all source files.
export const addresses: Record<string, Hex> = {
Nexus: "0x776d63154D2aa9256D72C420416c930F3B735464",
K1Validator: "0xd98238BBAeA4f91683d250003799EAd31d7F5c55",
K1ValidatorFactory: "0x8025afaD10209b8bEF3A3C94684AaE4D309c9996"
K1ValidatorFactory: "0x8025afaD10209b8bEF3A3C94684AaE4D309c9996",
UniActionPolicy: TEST_CONTRACTS.UniActionPolicy.address
} as const
export default addresses
15 changes: 13 additions & 2 deletions src/__contracts/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
import type { Hex } from "viem"
import { EntrypointAbi, K1ValidatorAbi, K1ValidatorFactoryAbi } from "./abi"
import {
EntrypointAbi,
K1ValidatorAbi,
K1ValidatorFactoryAbi,
UniActionPolicyAbi
} from "./abi"
import addresses from "./addresses"

export const ENTRYPOINT_SIMULATIONS: Hex =
"0x74Cb5e4eE81b86e70f9045036a1C5477de69eE87"
export const ENTRYPOINT_ADDRESS: Hex =
"0x0000000071727De22E5E9d8BAf0edAc6f37da032"

const uniActionPolicy = {
address: addresses.UniActionPolicy,
abi: UniActionPolicyAbi
} as const

const entryPoint = {
address: ENTRYPOINT_ADDRESS,
abi: EntrypointAbi
Expand All @@ -30,7 +40,8 @@ export const contracts = {
entryPoint,
entryPointSimulations,
k1ValidatorFactory,
k1Validator
k1Validator,
uniActionPolicy
} as const

export default contracts
2 changes: 2 additions & 0 deletions src/account/utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export const DefaultGasLimit = {
}

export const ERROR_MESSAGES = {
INVALID_HEX:
"Invalid hex, if you are targeting a number, consider using pad() and toHex() from viem: pad(toHex(BigInt(2000))",
ACCOUNT_NOT_DEPLOYED: "Account has not yet been deployed",
ACCOUNT_ALREADY_DEPLOYED: "Account already deployed",
NO_NATIVE_TOKEN_BALANCE_DURING_DEPLOY:
Expand Down
119 changes: 119 additions & 0 deletions src/modules/smartSessions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
import { type Address, type Hex, encodeAbiParameters, isHex } from "viem"
import { TEST_CONTRACTS } from "../../tests/src/callDatas"
import contracts from "../__contracts"
import { ERROR_MESSAGES } from "../account"

export type ActionConfig = {
valueLimitPerUse: bigint
paramRules: {
length: number
rules: {
condition: ParamCondition
offsetIndex: number
isLimited: boolean
ref: Hex
usage: LimitUsage
}[]
}
}

export const toActionConfig = (config: ActionConfig): RawActionConfig => ({
valueLimitPerUse: BigInt(config.valueLimitPerUse),
paramRules: {
length: BigInt(config.paramRules.length),
rules: config.paramRules.rules.map((rule) => {
if (isHex(rule.ref) && rule.ref.length !== 66) {
throw new Error(ERROR_MESSAGES.INVALID_HEX)
}
return {
condition: rule.condition,
offset: BigInt(rule.offsetIndex) * BigInt(32),
isLimited: rule.isLimited,
ref: rule.ref,
usage: rule.usage
}
})
}
})

export type RawActionConfig = {
valueLimitPerUse: bigint
paramRules: RawParamRules
}

export type RawParamRules = {
length: bigint
rules: RawParamRule[]
}

export type RawParamRule = {
condition: ParamCondition
offset: bigint
isLimited: boolean
ref: Hex
usage: LimitUsage
}

export type LimitUsage = {
limit: bigint
used: bigint
}

export enum ParamCondition {
EQUAL = 0,
GREATER_THAN = 1,
LESS_THAN = 2,
GREATER_THAN_OR_EQUAL = 3,
LESS_THAN_OR_EQUAL = 4,
NOT_EQUAL = 5
}

export type Policy = {
address: Hex
initData: Hex
deInitData: Hex
}

export type Params = {
token: Address
limit: bigint
}[]

export const MAX_RULES = 16

export const toUniversalActionPolicy = (
actionConfig: ActionConfig
): Policy => ({
address: contracts.uniActionPolicy.address,
initData: encodeAbiParameters(contracts.uniActionPolicy.abi, [
toActionConfig(actionConfig)
]),
deInitData: "0x"
})

export const sudoPolicy: Policy = {
address: "0x",
initData: "0x",
deInitData: "0x"
}

export const toSpendingLimitsPolicy = (params: Params): Policy => {
return {
address: TEST_CONTRACTS.ValueLimitPolicy.address,
initData: encodeAbiParameters(
[{ type: "address[]" }, { type: "uint256[]" }],
[params.map(({ token }) => token), params.map(({ limit }) => limit)]
),
deInitData: "0x"
}
}

export const policies = {
to: {
universalAction: toUniversalActionPolicy,
spendingLimits: toSpendingLimitsPolicy
},
sudo: sudoPolicy
} as const

export default policies
101 changes: 33 additions & 68 deletions src/modules/utils/Helper.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import {
type Address,

Check failure on line 2 in src/modules/utils/Helper.ts

View workflow job for this annotation

GitHub Actions / size report

'Address' is declared but its value is never read.
type ByteArray,
type Chain,
type Hex,
encodeAbiParameters,
keccak256,
parseAbiParameters
pad,
parseAbiParameters,
toHex
} from "viem"
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts"
import { type UserOperationStruct, getChain } from "../../account"
Expand Down Expand Up @@ -160,77 +163,39 @@ export const parseChain = (chainInfo: ChainInfo): Chain => {
if (typeof chainInfo === "number") return getChain(chainInfo)
return chainInfo
}

export type HardcodedReference = {
raw: Hex
}
type BaseReferenceValue = string | number | bigint | boolean | ByteArray
export type AnyReferenceValue = BaseReferenceValue | HardcodedReference
/**
*
* SessionSearchParam - The arguments that can be used to reconstruct a session object
*
* It can be one of the following:
* A session object {@link Session}
* A session storage client {@link ISessionStorage}
* A smart account address {@link Address}
*
* When a session object is provided, it is returned as is
* When a session storage client is provided, the session object is reconstructed from the session storage client using **all** of the sessionIds found in the storage client
* When a smart account address is provided, the default session storage client is used to reconstruct the session object using **all** of the sessionIds found in the storage client
*
*/
// export type SessionSearchParam = Session | ISessionStorage | Address
// export const didProvideFullSession = (
// searchParam: SessionSearchParam
// ): boolean => !!(searchParam as Session)?.sessionIDInfo?.length
/**
*
* reconstructSession - Reconstructs a session object from the provided arguments
* parseReferenceValue
*
* If a session object is provided, it is returned as is
* If a session storage client is provided, the session object is reconstructed from the session storage client using **all** of the sessionIds found in the storage client
* If a smart account address is provided, the default session storage client is used to reconstruct the session object using **all** of the sessionIds found in the storage client
* Parses the reference value to a hex string.
* The reference value can be hardcoded using the {@link HardcodedReference} type.
* Otherwise, it can be a string, number, bigint, boolean, or ByteArray.
*
* @param searchParam - This can be a session object {@link Session}, a session storage client {@link ISessionStorage} or a smart account address {@link Address}
* @returns A session object
* @error If the provided arguments do not match any of the above cases
* @param referenceValue {@link AnyReferenceValue}
* @returns Hex
*/
// export const resumeSession = async (
// searchParam: SessionSearchParam
// ): Promise<Session> => {
// const providedFullSession = didProvideFullSession(searchParam)
// const providedStorageClient = !!(searchParam as ISessionStorage)
// .smartAccountAddress?.length
// const providedSmartAccountAddress = isAddress(searchParam as Address)

// if (providedFullSession) {
// const session = searchParam as Session
// return session
// }
// if (providedStorageClient) {
// const sessionStorageClient = searchParam as ISessionStorage
// const leafArray = await sessionStorageClient.getAllSessionData()
// const sessionIDInfo = leafArray.map(({ sessionID }) => sessionID as string)
// const session: Session = {
// sessionIDInfo,
// sessionStorageClient
// }
// return session
// }
// if (providedSmartAccountAddress) {
// const smartAccountAddress = searchParam as Address
// // Use the default session storage client
// const sessionStorageClient = getDefaultStorageClient(smartAccountAddress)
// const leafArray = await sessionStorageClient.getAllSessionData()
// const sessionIDInfo = leafArray.map(({ sessionID }) => sessionID as string)
// const session: Session = {
// sessionIDInfo,
// sessionStorageClient
// }
// return session
// }
// throw new Error(ERROR_MESSAGES.UNKNOW_SESSION_ARGUMENTS)
// }

export const toTransaction = (execution: Execution): Transaction => {
return {
to: execution.target,
value: Number(execution.value),
data: execution.callData
export function parseReferenceValue(referenceValue: AnyReferenceValue): Hex {
try {
if ((referenceValue as HardcodedReference)?.raw) {
return (referenceValue as HardcodedReference)?.raw
}
if (typeof referenceValue === "bigint") {
return pad(toHex(referenceValue), { size: 32 }) as Hex
}
if (typeof referenceValue === "number") {
return pad(toHex(BigInt(referenceValue)), { size: 32 }) as Hex
}
if (typeof referenceValue === "boolean") {
return pad(toHex(referenceValue), { size: 32 }) as Hex
}
return pad(referenceValue as Hex, { size: 32 })
} catch (e) {
return pad(referenceValue as Hex, { size: 32 })
}
}
16 changes: 8 additions & 8 deletions tests/modules.ownableExecutor.read.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ describe("modules.ownable.executor.read", () => {
expect(balanceAfter - balanceBefore).toBe(1n)
})

test.skip("should initialize Ownable Executor Module with correct owners", async () => {
const ownableExecutorModule = await createOwnableExecutorModule(
smartAccount,
OWNABLE_EXECUTOR
)
const owners = await ownableExecutorModule.getOwners(OWNABLE_EXECUTOR)
expect(owners).toStrictEqual(ownableExecutorModule.owners)
})
// test.skip("should initialize Ownable Executor Module with correct owners", async () => {
// const ownableExecutorModule = await createOwnableExecutorModule(
// smartAccount,
// OWNABLE_EXECUTOR
// )
// const owners = await ownableExecutorModule.getOwners(OWNABLE_EXECUTOR)
// expect(owners).toStrictEqual(ownableExecutorModule.owners)
// })
})
Loading

0 comments on commit f657548

Please sign in to comment.