From b77704b1d6b113c4ae8c88f3bb9315aa607645b3 Mon Sep 17 00:00:00 2001 From: zer0dot Date: Thu, 27 Mar 2025 18:07:09 -0400 Subject: [PATCH 1/4] feat: (WIP) working on permissions --- .../aa-sdk/core/functions/tracingHeader.mdx | 43 ------------------- .../BaseAlchemySigner/toSolanaSigner.mdx | 41 ------------------ 2 files changed, 84 deletions(-) delete mode 100644 site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx delete mode 100644 site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx diff --git a/site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx b/site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx deleted file mode 100644 index 5e0d1e34d3..0000000000 --- a/site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx +++ /dev/null @@ -1,43 +0,0 @@ ---- -# This file is autogenerated - -title: tracingHeader -description: Overview of the tracingHeader method ---- - -# tracingHeader - -We wanted a transport that is adding the add headers for the headers that we are sending to the server for tracing cross actions -and services. - -## Import - -```ts -import { tracingHeader } from "@aa-sdk/core"; -``` - -## Usage - -```ts -import { createPublicClient, http } from "viem"; -import { tracingHeader } from "@aa-sdk/core"; - -const clientWithTracing = createPublicClient({ - transport: tracingHeader({ - breadcrumb: "myMethodOrAction", - transport: http(OTHER_RPC_URL), - }), -}); -``` - -## Parameters - -### params - -`TracingHeadersParams` -tracing headers config to wrap around a transport to add in headers for tracing - -## Returns - -`CustomTransport` -a viem Transport that splits traffic diff --git a/site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx b/site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx deleted file mode 100644 index 1c7420ca45..0000000000 --- a/site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx +++ /dev/null @@ -1,41 +0,0 @@ ---- -# This file is autogenerated - -title: toSolanaSigner -description: Overview of the toSolanaSigner method ---- - -# toSolanaSigner - -Creates a new instance of `SolanaSigner` using the provided inner value. -This requires the signer to be authenticated first - -## Import - -```ts -import { BaseAlchemySigner } from "@account-kit/signer"; -``` - -## Usage - -```ts -import { AlchemyWebSigner } from "@account-kit/signer"; - -const signer = new AlchemyWebSigner({ - client: { - connection: { - rpcUrl: "/api/rpc", - }, - iframeConfig: { - iframeContainerId: "alchemy-signer-iframe-container", - }, - }, -}); - -const solanaSigner = signer.toSolanaSigner(); -``` - -## Returns - -`SolanaSigner` -A new instance of `SolanaSigner` From 95bcd21750a0d07b119925e6c92d8755a80c5fd3 Mon Sep 17 00:00:00 2001 From: zer0dot Date: Fri, 28 Mar 2025 17:25:55 -0400 Subject: [PATCH 2/4] feat: add mav2 account/client mode, via deferred action --- .../account/common/modularAccountV2Base.ts | 61 +++++-- .../src/ma-v2/account/modularAccountV2.ts | 4 + .../src/ma-v2/account/nativeSMASigner.ts | 34 ++-- .../src/ma-v2/actions/deferralActions.test.ts | 156 ++++++++++++++++++ .../src/ma-v2/client/client.test.ts | 3 - .../single-signer-validation/signer.ts | 32 ++-- .../src/ma-v2/permissionBuilder.ts | 19 ++- .../aa-sdk/core/functions/tracingHeader.mdx | 43 +++++ .../BaseAlchemySigner/toSolanaSigner.mdx | 41 +++++ 9 files changed, 350 insertions(+), 43 deletions(-) create mode 100644 account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts create mode 100644 site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx create mode 100644 site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx diff --git a/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts b/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts index 407bf4e580..86c77fce86 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts @@ -58,6 +58,12 @@ export type ValidationDataParams = entityId: number; }; +export type DeferredAction = { + data: Hex; + hasAssociatedExecHooks: boolean; + nonce: bigint; +}; + export type ModularAccountV2< TSigner extends SmartAccountSigner = SmartAccountSigner > = SmartContractAccountWithSigner<"ModularAccountV2", TSigner, "0.7.0"> & { @@ -85,6 +91,7 @@ export type CreateMAV2BaseParams< signer: TSigner; signerEntity?: SignerEntity; accountAddress: Address; + deferredAction?: DeferredAction; }; export type CreateMAV2BaseReturnType< @@ -108,6 +115,7 @@ export async function createMAv2Base< entityId = DEFAULT_OWNER_ENTITY_ID, } = {}, accountAddress, + deferredAction, ...remainingToSmartContractAccountParams } = config; @@ -120,6 +128,30 @@ export async function createMAv2Base< chain, }); + const entryPointContract = getContract({ + address: entryPoint.address, + abi: entryPoint.abi, + client, + }); + + let useDeferredAction: boolean = false; + + if (deferredAction) { + // Set these values if the deferred action has not been consumed. We check this with the EP + const nextNonceForDeferredActionNonce: bigint = + (await entryPointContract.read.getNonce([ + accountAddress, + deferredAction.nonce >> 64n, + ])) as bigint; + + // we only add the deferred action in if the nonce has not been consumed + if (deferredAction.nonce === nextNonceForDeferredActionNonce) { + useDeferredAction = true; + } else if (deferredAction.nonce > nextNonceForDeferredActionNonce) { + throw new Error("Deferred action nonce invalid"); + } + } + const encodeExecute: (tx: AccountOp) => Promise = async ({ target, data, @@ -150,18 +182,16 @@ export async function createMAv2Base< const isAccountDeployed: () => Promise = async () => !!(await client.getCode({ address: accountAddress })); - // TODO: add deferred action flag + const getNonce = async (nonceKey: bigint = 0n): Promise => { + if (useDeferredAction && deferredAction) { + return deferredAction.nonce; + } + if (nonceKey > maxUint152) { throw new InvalidNonceKeyError(nonceKey); } - const entryPointContract = getContract({ - address: entryPoint.address, - abi: entryPoint.abi, - client, - }); - const fullNonceKey: bigint = (nonceKey << 40n) + (BigInt(entityId) << 8n) + @@ -216,11 +246,16 @@ export async function createMAv2Base< entityId: Number(entityId), }); - return validationData.executionHooks.length + return (useDeferredAction && deferredAction?.hasAssociatedExecHooks) || + validationData.executionHooks.length ? concatHex([executeUserOpSelector, callData]) : callData; }; + const deferredActionData = useDeferredAction + ? deferredAction?.data + : undefined; + const baseAccount = await toSmartContractAccount({ ...remainingToSmartContractAccountParams, transport, @@ -231,8 +266,14 @@ export async function createMAv2Base< encodeBatchExecute, getNonce, ...(entityId === DEFAULT_OWNER_ENTITY_ID - ? nativeSMASigner(signer, chain, accountAddress) - : singleSignerMessageSigner(signer, chain, accountAddress, entityId)), + ? nativeSMASigner(signer, chain, accountAddress, deferredActionData) + : singleSignerMessageSigner( + signer, + chain, + accountAddress, + entityId, + deferredActionData + )), }); return { diff --git a/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts index 044621f8b5..efeeae0254 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts @@ -23,6 +23,7 @@ import { getDefaultMAV2FactoryAddress } from "../utils.js"; import { type SignerEntity, type ModularAccountV2, + type DeferredAction, createMAv2Base, } from "./common/modularAccountV2Base.js"; import { DEFAULT_OWNER_ENTITY_ID } from "../utils.js"; @@ -36,6 +37,7 @@ export type CreateModularAccountV2Params< > & { signer: TSigner; entryPoint?: EntryPointDef<"0.7.0", Chain>; + deferredAction?: DeferredAction; signerEntity?: SignerEntity; }) & ( @@ -103,6 +105,7 @@ export async function createModularAccountV2( entityId: DEFAULT_OWNER_ENTITY_ID, }, signerEntity: { entityId = DEFAULT_OWNER_ENTITY_ID } = {}, + deferredAction, } = config; const client = createBundlerClient({ @@ -183,6 +186,7 @@ export async function createModularAccountV2( signer, entryPoint, signerEntity, + deferredAction, ...accountFunctions, }); } diff --git a/account-kit/smart-contracts/src/ma-v2/account/nativeSMASigner.ts b/account-kit/smart-contracts/src/ma-v2/account/nativeSMASigner.ts index 6ba64d4745..476e7fa174 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/nativeSMASigner.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/nativeSMASigner.ts @@ -9,6 +9,7 @@ import { type Chain, type Address, concat, + concatHex, } from "viem"; import { @@ -23,7 +24,6 @@ import { SignatureType } from "../modules/utils.js"; * @example * ```ts * import { nativeSMASigner } from "@account-kit/smart-contracts"; - * import { LocalAccountSigner } from "@aa-sdk/core"; * * const MNEMONIC = "...": @@ -38,31 +38,37 @@ import { SignatureType } from "../modules/utils.js"; * @param {SmartAccountSigner} signer Signer to use for signing operations * @param {Chain} chain Chain object for the signer * @param {Address} accountAddress address of the smart account using this signer + * @param {Hex} deferredActionData optional deferred action data to prepend to the uo signatures * @returns {object} an object with methods for signing operations and managing signatures */ export const nativeSMASigner = ( signer: SmartAccountSigner, chain: Chain, - accountAddress: Address + accountAddress: Address, + deferredActionData?: Hex ) => { return { getDummySignature: (): Hex => { - const dummyEcdsaSignature = - "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; - - return packUOSignature({ + const sig = packUOSignature({ // orderedHookData: [], - validationSignature: dummyEcdsaSignature, + validationSignature: + "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c", }); + + return deferredActionData ? concatHex([deferredActionData, sig]) : sig; }, - signUserOperationHash: (uoHash: Hex): Promise => { - return signer.signMessage({ raw: uoHash }).then((signature: Hex) => - packUOSignature({ - // orderedHookData: [], - validationSignature: signature, - }) - ); + signUserOperationHash: async (uoHash: Hex): Promise => { + const sig = await signer + .signMessage({ raw: uoHash }) + .then((signature: Hex) => + packUOSignature({ + // orderedHookData: [], + validationSignature: signature, + }) + ); + + return deferredActionData ? concatHex([deferredActionData, sig]) : sig; }, // we apply the expected 1271 packing here since the account contract will expect it diff --git a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts new file mode 100644 index 0000000000..6a98c013a9 --- /dev/null +++ b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts @@ -0,0 +1,156 @@ +import { + erc7677Middleware, + LocalAccountSigner, + type SmartAccountSigner, +} from "@aa-sdk/core"; +import { + custom, + parseEther, + publicActions, + testActions, + type TestActions, +} from "viem"; +import { installValidationActions } from "@account-kit/smart-contracts/experimental"; +import { + // createModularAccountV2Client, + type SignerEntity, +} from "@account-kit/smart-contracts"; +import { local070Instance } from "~test/instances.js"; +import { setBalance } from "viem/actions"; +import { accounts } from "~test/constants.js"; +import { alchemyGasAndPaymasterAndDataMiddleware } from "@account-kit/infra"; +import { deferralActions } from "./deferralActions.js"; +import { PermissionBuilder, PermissionType } from "../permissionBuilder.js"; +import { createModularAccountV2Client } from "../client/client.js"; + +// Note: These tests maintain a shared state to not break the local-running rundler by desyncing the chain. +describe("MA v2 deferral actions tests", async () => { + const instance = local070Instance; + + let client: ReturnType & + ReturnType & + TestActions; + + beforeAll(async () => { + client = instance + .getClient() + .extend(publicActions) + .extend(testActions({ mode: "anvil" })); + }); + + const signer: SmartAccountSigner = new LocalAccountSigner( + accounts.fundedAccountOwner + ); + + const target = "0x000000000000000000000000000000000000dEaD"; + const sendAmount = parseEther("1"); + + it("tests the full deferred actions flow", async () => { + const provider = (await givenConnectedProvider({ signer })) + .extend(deferralActions) + .extend(installValidationActions); + + await setBalance(instance.getClient(), { + address: provider.getAddress(), + value: parseEther("2"), + }); + + const sessionKeyEntityId = 1; + const sessionKey: SmartAccountSigner = new LocalAccountSigner( + accounts.unfundedAccountOwner + ); + + // these can be default values or from call arguments + const { entityId, nonce } = await provider.getEntityIdAndNonce({ + entityId: 0, + nonceKey: 0n, + isGlobalValidation: true, + }); + + // TODO: remove nonce override here + const { typedData, hasAssociatedExecHooks } = await new PermissionBuilder( + provider + ) + .configure({ + key: { + publicKey: await sessionKey.getAddress(), + type: "secp256k1", + }, + entityId, + nonceKeyOverride: 0n, // TODO: add nonce override here + }) + .addPermission({ + permission: { + type: PermissionType.ROOT, + }, + }) + .compile_deferred({ + deadline: 0, + uoValidationEntityId: entityId, + uoIsGlobalValidation: true, + }); + + const sig = await provider.account.signTypedData(typedData); + + const deferredActionDigest = await provider.buildDeferredActionDigest({ + typedData, + sig, + }); + + // TODO: need nonce, orig account address, orig account initcode, signer entity + const sessionKeyClient = await createModularAccountV2Client({ + transport: custom(instance.getClient()), + chain: instance.chain, + accountAddress: provider.getAddress(), + signer: sessionKey, + signerEntity: { isGlobalValidation: true, entityId: sessionKeyEntityId }, + initCode: provider.account.getInitCode(), + deferredAction: { + data: deferredActionDigest, + nonce, + hasAssociatedExecHooks, + }, + }); + + const uoResult = await sessionKeyClient.sendUserOperation({ + uo: { + target: target, + value: sendAmount, + data: "0x", + }, + }); + + await provider.waitForUserOperationTransaction(uoResult); + }); + + const givenConnectedProvider = async ({ + signer, + signerEntity, + accountAddress, + paymasterMiddleware, + salt = 0n, + }: { + signer: SmartAccountSigner; + signerEntity?: SignerEntity; + accountAddress?: `0x${string}`; + paymasterMiddleware?: "alchemyGasAndPaymasterAndData" | "erc7677"; + salt?: bigint; + }) => + createModularAccountV2Client({ + chain: instance.chain, + signer, + accountAddress, + signerEntity, + transport: custom(instance.getClient()), + ...(paymasterMiddleware === "alchemyGasAndPaymasterAndData" + ? alchemyGasAndPaymasterAndDataMiddleware({ + policyId: "FAKE_POLICY_ID", + // @ts-ignore (expects an alchemy transport, but we're using a custom transport for mocking) + transport: custom(instance.getClient()), + }) + : paymasterMiddleware === "erc7677" + ? erc7677Middleware() + : {}), + salt, + }); +}); diff --git a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts index 7f173b342d..deb969cf92 100644 --- a/account-kit/smart-contracts/src/ma-v2/client/client.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/client/client.test.ts @@ -595,9 +595,6 @@ describe("MA v2 Tests", async () => { uoIsGlobalValidation: isGlobalValidation, // UO validation is global }); - console.log(typedData); - console.log("Deferred: %s, \n\n Nonce: %s", typedData, nonceOverride); - // Sign the typed data using the first session key const deferredValidationSig = await sessionKeyClient.account.signTypedData( typedData diff --git a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts index 5d29b27e90..f0cd764ecb 100644 --- a/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts +++ b/account-kit/smart-contracts/src/ma-v2/modules/single-signer-validation/signer.ts @@ -39,32 +39,38 @@ import { packUOSignature, pack1271Signature } from "../../utils.js"; * @param {Chain} chain Chain object for the signer * @param {Address} accountAddress address of the smart account using this signer * @param {number} entityId the entity id of the signing validation + * @param {Hex} deferredActionData optional deferred action data to prepend to the uo signatures * @returns {object} an object with methods for signing operations and managing signatures */ export const singleSignerMessageSigner = ( signer: SmartAccountSigner, chain: Chain, accountAddress: Address, - entityId: number + entityId: number, + deferredActionData?: Hex ) => { return { getDummySignature: (): Hex => { - const dummyEcdsaSignature = - "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c"; - - return packUOSignature({ + const sig = packUOSignature({ // orderedHookData: [], - validationSignature: dummyEcdsaSignature, + validationSignature: + "0xfffffffffffffffffffffffffffffff0000000000000000000000000000000007aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa1c", }); + + return deferredActionData ? concatHex([deferredActionData, sig]) : sig; }, - signUserOperationHash: (uoHash: Hex): Promise => { - return signer.signMessage({ raw: uoHash }).then((signature: Hex) => - packUOSignature({ - // orderedHookData: [], - validationSignature: signature, - }) - ); + signUserOperationHash: async (uoHash: Hex): Promise => { + const sig = await signer + .signMessage({ raw: uoHash }) + .then((signature: Hex) => + packUOSignature({ + // orderedHookData: [], + validationSignature: signature, + }) + ); + + return deferredActionData ? concatHex([deferredActionData, sig]) : sig; }, // we apply the expected 1271 packing here since the account contract will expect it diff --git a/account-kit/smart-contracts/src/ma-v2/permissionBuilder.ts b/account-kit/smart-contracts/src/ma-v2/permissionBuilder.ts index c70d071687..0639733eec 100644 --- a/account-kit/smart-contracts/src/ma-v2/permissionBuilder.ts +++ b/account-kit/smart-contracts/src/ma-v2/permissionBuilder.ts @@ -11,7 +11,7 @@ import { import type { ModularAccountV2Client } from "./client/client.js"; import { deferralActions, - type DeferredActionReturnData, + type DeferredActionTypedData, } from "./actions/deferralActions.js"; import { NativeTokenLimitModule } from "./modules/native-token-limit-module/module.js"; import { @@ -208,6 +208,7 @@ export class PermissionBuilder { private permissions: Permission[] = []; private hooks: Hook[] = []; private nonceKeyOverride: bigint = 0n; + private hasAssociatedExecHooks: boolean = false; constructor(client: ModularAccountV2Client) { this.client = client; @@ -335,7 +336,11 @@ export class PermissionBuilder { deadline: number; uoValidationEntityId: number; uoIsGlobalValidation: boolean; - }): Promise { + }): Promise<{ + typedData: DeferredActionTypedData; + hasAssociatedExecHooks: boolean; + nonceOverride: bigint; + }> { this.validateConfiguration(); // Maybe add checks, like zero address module addr @@ -363,7 +368,7 @@ export class PermissionBuilder { const installValidationCall = await this.compile_raw(); - return await deferralActions( + const { typedData, nonceOverride } = await deferralActions( this.client ).createDeferredActionTypedDataObject({ callData: installValidationCall, @@ -372,6 +377,12 @@ export class PermissionBuilder { uoIsGlobalValidation: uoIsGlobalValidation, nonceKeyOverride: this.nonceKeyOverride, }); + + return { + typedData, + nonceOverride, + hasAssociatedExecHooks: this.hasAssociatedExecHooks, + }; } // Use for direct `installValidation()` low-level calls (maybe useless) @@ -454,6 +465,7 @@ export class PermissionBuilder { spendLimit: BigInt(permission.data.allowance), }, }; + this.hasAssociatedExecHooks = true; break; case PermissionType.ERC20_TOKEN_TRANSFER: if (permission.data.address === zeroAddress) { @@ -485,6 +497,7 @@ export class PermissionBuilder { ], }, }; + this.hasAssociatedExecHooks = true; // Also allow `approve` and `transfer` for the erc20 rawHooks[HookIdentifier.PREVAL_ALLOWLIST] = { hookConfig: { diff --git a/site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx b/site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx new file mode 100644 index 0000000000..5e0d1e34d3 --- /dev/null +++ b/site/pages/reference/aa-sdk/core/functions/tracingHeader.mdx @@ -0,0 +1,43 @@ +--- +# This file is autogenerated + +title: tracingHeader +description: Overview of the tracingHeader method +--- + +# tracingHeader + +We wanted a transport that is adding the add headers for the headers that we are sending to the server for tracing cross actions +and services. + +## Import + +```ts +import { tracingHeader } from "@aa-sdk/core"; +``` + +## Usage + +```ts +import { createPublicClient, http } from "viem"; +import { tracingHeader } from "@aa-sdk/core"; + +const clientWithTracing = createPublicClient({ + transport: tracingHeader({ + breadcrumb: "myMethodOrAction", + transport: http(OTHER_RPC_URL), + }), +}); +``` + +## Parameters + +### params + +`TracingHeadersParams` +tracing headers config to wrap around a transport to add in headers for tracing + +## Returns + +`CustomTransport` +a viem Transport that splits traffic diff --git a/site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx b/site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx new file mode 100644 index 0000000000..1c7420ca45 --- /dev/null +++ b/site/pages/reference/account-kit/signer/classes/BaseAlchemySigner/toSolanaSigner.mdx @@ -0,0 +1,41 @@ +--- +# This file is autogenerated + +title: toSolanaSigner +description: Overview of the toSolanaSigner method +--- + +# toSolanaSigner + +Creates a new instance of `SolanaSigner` using the provided inner value. +This requires the signer to be authenticated first + +## Import + +```ts +import { BaseAlchemySigner } from "@account-kit/signer"; +``` + +## Usage + +```ts +import { AlchemyWebSigner } from "@account-kit/signer"; + +const signer = new AlchemyWebSigner({ + client: { + connection: { + rpcUrl: "/api/rpc", + }, + iframeConfig: { + iframeContainerId: "alchemy-signer-iframe-container", + }, + }, +}); + +const solanaSigner = signer.toSolanaSigner(); +``` + +## Returns + +`SolanaSigner` +A new instance of `SolanaSigner` From 2e79058ae4c71089b2cebff8fff1d539edc09012 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Thu, 3 Apr 2025 17:54:22 -0400 Subject: [PATCH 3/4] chore: add todos --- .../smart-contracts/src/ma-v2/actions/deferralActions.test.ts | 4 ++-- .../smart-contracts/src/ma-v2/actions/deferralActions.ts | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts index 6a98c013a9..c916338c36 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts @@ -97,14 +97,14 @@ describe("MA v2 deferral actions tests", async () => { sig, }); - // TODO: need nonce, orig account address, orig account initcode, signer entity + // TODO: need nonce, orig account address, orig account initcode const sessionKeyClient = await createModularAccountV2Client({ transport: custom(instance.getClient()), chain: instance.chain, accountAddress: provider.getAddress(), signer: sessionKey, - signerEntity: { isGlobalValidation: true, entityId: sessionKeyEntityId }, initCode: provider.account.getInitCode(), + // TODO: change to Hex deferredAction: { data: deferredActionDigest, nonce, diff --git a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.ts b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.ts index 71aa269180..274e1ff419 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.ts @@ -182,6 +182,7 @@ export const deferralActions: ( [validationLocator, typedData.message.deadline, typedData.message.call] ); + // TODO: encoded call data as input const encodedDataLength = size(encodedCallData); const sigLength = size(sig); From 05e16e2ea919b9ea94fdab661b16ff5007824ba1 Mon Sep 17 00:00:00 2001 From: howydev <132113803+howydev@users.noreply.github.com> Date: Thu, 3 Apr 2025 20:04:29 -0400 Subject: [PATCH 4/4] feat: change back to context hex input --- .../account/common/modularAccountV2Base.ts | 31 +++++++++---------- .../src/ma-v2/account/modularAccountV2.ts | 3 +- .../src/ma-v2/actions/deferralActions.test.ts | 11 ++++--- 3 files changed, 22 insertions(+), 23 deletions(-) diff --git a/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts b/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts index 86c77fce86..46fda0fdd8 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/common/modularAccountV2Base.ts @@ -58,12 +58,6 @@ export type ValidationDataParams = entityId: number; }; -export type DeferredAction = { - data: Hex; - hasAssociatedExecHooks: boolean; - nonce: bigint; -}; - export type ModularAccountV2< TSigner extends SmartAccountSigner = SmartAccountSigner > = SmartContractAccountWithSigner<"ModularAccountV2", TSigner, "0.7.0"> & { @@ -91,7 +85,7 @@ export type CreateMAV2BaseParams< signer: TSigner; signerEntity?: SignerEntity; accountAddress: Address; - deferredAction?: DeferredAction; + deferredAction?: Hex; }; export type CreateMAV2BaseReturnType< @@ -135,19 +129,28 @@ export async function createMAv2Base< }); let useDeferredAction: boolean = false; + let nonce: bigint = 0n; + let deferredActionData: Hex = "0x"; + let hasAssociatedExecHooks: boolean = false; if (deferredAction) { + if (deferredAction.slice(3, 5) !== "00") { + throw new Error("Invalid deferred action version"); + } + nonce = BigInt(`0x${deferredAction.slice(6, 70)}`); // Set these values if the deferred action has not been consumed. We check this with the EP const nextNonceForDeferredActionNonce: bigint = (await entryPointContract.read.getNonce([ accountAddress, - deferredAction.nonce >> 64n, + nonce >> 64n, ])) as bigint; // we only add the deferred action in if the nonce has not been consumed - if (deferredAction.nonce === nextNonceForDeferredActionNonce) { + if (nonce === nextNonceForDeferredActionNonce) { useDeferredAction = true; - } else if (deferredAction.nonce > nextNonceForDeferredActionNonce) { + deferredActionData = `0x${deferredAction.slice(70)}`; + hasAssociatedExecHooks = deferredAction[6] === "1"; + } else if (nonce > nextNonceForDeferredActionNonce) { throw new Error("Deferred action nonce invalid"); } } @@ -185,7 +188,7 @@ export async function createMAv2Base< const getNonce = async (nonceKey: bigint = 0n): Promise => { if (useDeferredAction && deferredAction) { - return deferredAction.nonce; + return nonce; } if (nonceKey > maxUint152) { @@ -246,16 +249,12 @@ export async function createMAv2Base< entityId: Number(entityId), }); - return (useDeferredAction && deferredAction?.hasAssociatedExecHooks) || + return (useDeferredAction && hasAssociatedExecHooks) || validationData.executionHooks.length ? concatHex([executeUserOpSelector, callData]) : callData; }; - const deferredActionData = useDeferredAction - ? deferredAction?.data - : undefined; - const baseAccount = await toSmartContractAccount({ ...remainingToSmartContractAccountParams, transport, diff --git a/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts b/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts index efeeae0254..681fc8e78b 100644 --- a/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts +++ b/account-kit/smart-contracts/src/ma-v2/account/modularAccountV2.ts @@ -23,7 +23,6 @@ import { getDefaultMAV2FactoryAddress } from "../utils.js"; import { type SignerEntity, type ModularAccountV2, - type DeferredAction, createMAv2Base, } from "./common/modularAccountV2Base.js"; import { DEFAULT_OWNER_ENTITY_ID } from "../utils.js"; @@ -37,7 +36,7 @@ export type CreateModularAccountV2Params< > & { signer: TSigner; entryPoint?: EntryPointDef<"0.7.0", Chain>; - deferredAction?: DeferredAction; + deferredAction?: Hex; signerEntity?: SignerEntity; }) & ( diff --git a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts index c916338c36..1e31b50dc0 100644 --- a/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts +++ b/account-kit/smart-contracts/src/ma-v2/actions/deferralActions.test.ts @@ -8,6 +8,7 @@ import { parseEther, publicActions, testActions, + toHex, type TestActions, } from "viem"; import { installValidationActions } from "@account-kit/smart-contracts/experimental"; @@ -104,12 +105,12 @@ describe("MA v2 deferral actions tests", async () => { accountAddress: provider.getAddress(), signer: sessionKey, initCode: provider.account.getInitCode(), - // TODO: change to Hex - deferredAction: { - data: deferredActionDigest, + deferredAction: `0x00${hasAssociatedExecHooks ? "01" : "00"}${toHex( nonce, - hasAssociatedExecHooks, - }, + { + size: 32, + } + ).slice(2)}${deferredActionDigest.slice(2)}`, }); const uoResult = await sessionKeyClient.sendUserOperation({