From 000d7919a930680fd924c411aabb0544ae210fac Mon Sep 17 00:00:00 2001 From: Patchy Bot Date: Mon, 16 Feb 2026 23:46:19 +0000 Subject: [PATCH 1/2] Replace accountMetadata with dependencies in ERC-7710 delegation actions --- ...710sendUserOperationWithDelegation.test.ts | 11 ++--- .../actions/erc7710RedeemDelegationAction.ts | 41 ++++++++----------- .../erc7710RedeemDelegationAction.test.ts | 32 +++++++-------- 3 files changed, 40 insertions(+), 44 deletions(-) diff --git a/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts b/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts index 5f70e75a..b21fb0b1 100644 --- a/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts +++ b/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts @@ -119,6 +119,7 @@ test('maincase: Bob redeems the delegation in order to call increment() on the c }, ], ...gasPrice, + dependencies: [{ factory, factoryData }], }); const receipt = await bundlerClient.waitForUserOperationReceipt({ @@ -187,7 +188,7 @@ test('Bob redeems the delegation in order to call increment() on the counter con expect(targetAddressBalance).toEqual(10n); }); -test('Bob redeems the delegation, and deploys Alices smart account via accountMetadata', async () => { +test('Bob redeems the delegation, and deploys Alices smart account via dependencies', async () => { await fundAddress(bobSmartAccount.address); const counterContract = getContract({ @@ -236,7 +237,7 @@ test('Bob redeems the delegation, and deploys Alices smart account via accountMe expect(countAfter).toEqual(1n); }); -test('Bob redeems the delegation, with account metadata, even though Alices account is already deployed', async () => { +test('Bob redeems the delegation, with dependencies, even though Alices account is already deployed', async () => { await fundAddress(bobSmartAccount.address); const counterContract = getContract({ @@ -287,7 +288,7 @@ test('Bob redeems the delegation, with account metadata, even though Alices acco expect(countAfter).toEqual(1n); }); -test('Bob calls sendUserOperationWithDelegation with the same accountMetadata multiple times', async () => { +test('Bob calls sendUserOperationWithDelegation with the same dependencies multiple times', async () => { await fundAddress(bobSmartAccount.address); const { factory, factoryData } = await aliceSmartAccount.getFactoryArgs(); @@ -305,7 +306,7 @@ test('Bob calls sendUserOperationWithDelegation with the same accountMetadata mu }, ], ...gasPrice, - accountMetadata: [ + dependencies: [ { factory, factoryData }, { factory, factoryData }, ], @@ -331,7 +332,7 @@ test('Bob calls sendUserOperationWithDelegation with the same accountMetadata mu expect(aliceSmartAccountBalance).toEqual(10n); }); -// callData is disallowed, because if we attempt to re-encode with additional calls (ie accountMetadata) +// callData is disallowed, because if we attempt to re-encode with additional calls (ie dependencies) // the inner call will be targetting a function on the smart account, which is likely attributed with // OnlyEntryPoint. Because it's calling from the smart account, it will fail. test.skip('Bob attempts to call sendUserOperationWithDelegation with callData specified', async () => { diff --git a/packages/smart-accounts-kit/src/actions/erc7710RedeemDelegationAction.ts b/packages/smart-accounts-kit/src/actions/erc7710RedeemDelegationAction.ts index 6074c706..de8e4a41 100644 --- a/packages/smart-accounts-kit/src/actions/erc7710RedeemDelegationAction.ts +++ b/packages/smart-accounts-kit/src/actions/erc7710RedeemDelegationAction.ts @@ -97,7 +97,7 @@ export type SendUserOperationWithDelegationParameters< TAccount extends SmartAccount | undefined = SmartAccount | undefined, TAccountOverride extends SmartAccount | undefined = SmartAccount | undefined, > = SendUserOperationParameters & { - accountMetadata?: { factory: Hex; factoryData: Hex }[]; + dependencies?: { factory: Hex; factoryData: Hex }[]; calls: DelegatedCall[]; publicClient: PublicClient; }; @@ -131,7 +131,7 @@ export type SendUserOperationWithDelegationParameters< * delegationManager: '0x...', * }, * ], - * accountMetadata: [{ factory: '0x...', factoryData: '0x...' }], // Optional: for deploying accounts + * dependencies: [{ factory: '0x...', factoryData: '0x...' }], // Optional: for deploying accounts * }) */ export async function sendUserOperationWithDelegationAction< @@ -144,7 +144,7 @@ export async function sendUserOperationWithDelegationAction< TAccountOverride >, ) { - if (parameters.accountMetadata) { + if (parameters.dependencies) { const { publicClient } = parameters; const includedAccountKeys: Record = {}; @@ -157,29 +157,24 @@ export async function sendUserOperationWithDelegationAction< const { SimpleFactory } = getSmartAccountsEnvironment(chainId); - const uniqueAccountMetadatas = parameters.accountMetadata.filter( - (accountMetadata) => { - if (!isAddressEqual(accountMetadata.factory, SimpleFactory)) { - throw new Error( - `Invalid accountMetadata: ${accountMetadata.factory} is not allowed.`, - ); - } - - // ensure that factory calls are not duplicated - const accountKey = concat([ - accountMetadata.factory, - accountMetadata.factoryData, - ]); - const isDuplicate = includedAccountKeys[accountKey]; - - includedAccountKeys[accountKey] = true; - return !isDuplicate; - }, - ); + const uniqueDependencies = parameters.dependencies.filter((dependency) => { + if (!isAddressEqual(dependency.factory, SimpleFactory)) { + throw new Error( + `Invalid dependency: ${dependency.factory} is not allowed.`, + ); + } + + // ensure that factory calls are not duplicated + const accountKey = concat([dependency.factory, dependency.factoryData]); + const isDuplicate = includedAccountKeys[accountKey]; + + includedAccountKeys[accountKey] = true; + return !isDuplicate; + }); const factoryCalls = ( await Promise.all( - uniqueAccountMetadatas.map(async ({ factory, factoryData }) => { + uniqueDependencies.map(async ({ factory, factoryData }) => { const isDeployed = await publicClient .call({ to: factory, diff --git a/packages/smart-accounts-kit/test/actions/erc7710RedeemDelegationAction.test.ts b/packages/smart-accounts-kit/test/actions/erc7710RedeemDelegationAction.test.ts index cc4242d6..290f1a31 100644 --- a/packages/smart-accounts-kit/test/actions/erc7710RedeemDelegationAction.test.ts +++ b/packages/smart-accounts-kit/test/actions/erc7710RedeemDelegationAction.test.ts @@ -97,7 +97,7 @@ describe('erc7710RedeemDelegationAction', () => { ); }); - it('should append factory calls when accountMetadata is provided', async () => { + it('should append factory calls when dependencies is provided', async () => { const bundlerClient = createBundlerClient({ transport: custom({ request: mockBundlerRequest }), chain, @@ -115,7 +115,7 @@ describe('erc7710RedeemDelegationAction', () => { }, ]; - const accountMetadata = [ + const dependencies = [ { factory: simpleFactoryAddress, factoryData: randomBytes(128), @@ -129,7 +129,7 @@ describe('erc7710RedeemDelegationAction', () => { { publicClient, calls, - accountMetadata, + dependencies, }; await extendedBundlerClient.sendUserOperationWithDelegation( @@ -140,13 +140,13 @@ describe('erc7710RedeemDelegationAction', () => { ...sendUserOperationWithDelegationArgs, calls: [ { - to: accountMetadata[0]?.factory, - data: accountMetadata[0]?.factoryData, + to: dependencies[0]?.factory, + data: dependencies[0]?.factoryData, value: 0n, }, { - to: accountMetadata[1]?.factory, - data: accountMetadata[1]?.factoryData, + to: dependencies[1]?.factory, + data: dependencies[1]?.factoryData, value: 0n, }, ...calls, @@ -154,7 +154,7 @@ describe('erc7710RedeemDelegationAction', () => { }); }); - it('should throw an error when SimpleFactory is provided as accountMetadata factory', async () => { + it('should throw an error when SimpleFactory is provided as dependencies factory', async () => { const bundlerClient = createBundlerClient({ transport: custom({ request: mockBundlerRequest }), chain, @@ -170,7 +170,7 @@ describe('erc7710RedeemDelegationAction', () => { }, ]; - const accountMetadata = [ + const dependencies = [ { factory: randomAddress(), factoryData: randomBytes(128), @@ -181,10 +181,10 @@ describe('erc7710RedeemDelegationAction', () => { { publicClient, calls, - accountMetadata, + dependencies, }; - const factoryAddress = accountMetadata[0]?.factory; + const factoryAddress = dependencies[0]?.factory; if (!factoryAddress) { throw new Error('factoryAddress is not set'); @@ -195,7 +195,7 @@ describe('erc7710RedeemDelegationAction', () => { sendUserOperationWithDelegationArgs, ), ).rejects.toThrow( - `Invalid accountMetadata: ${factoryAddress} is not allowed.`, + `Invalid dependency: ${factoryAddress} is not allowed.`, ); }); @@ -217,7 +217,7 @@ describe('erc7710RedeemDelegationAction', () => { }, ]; - const accountMetadata = [ + const dependencies = [ { factory: simpleFactoryAddress, factoryData: randomBytes(128), @@ -238,7 +238,7 @@ describe('erc7710RedeemDelegationAction', () => { Chain >, calls, - accountMetadata, + dependencies, }; await extendedBundlerClient.sendUserOperationWithDelegation( @@ -246,8 +246,8 @@ describe('erc7710RedeemDelegationAction', () => { ); expect(mockPublicClient.call.firstCall.args[0]).to.deep.equal({ - to: accountMetadata[0]?.factory, - data: accountMetadata[0]?.factoryData, + to: dependencies[0]?.factory, + data: dependencies[0]?.factoryData, }); expect(sendUserOperationStub.firstCall.args[0]).to.deep.equal({ From 129e62272df5d1b84c414a498bc8f560f769d64e Mon Sep 17 00:00:00 2001 From: Patchy Bot Date: Tue, 17 Feb 2026 03:56:30 +0000 Subject: [PATCH 2/2] Remove undefined dependencies from sendUserOperationWithDelegation test --- .../test/actions/erc7710sendUserOperationWithDelegation.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts b/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts index b21fb0b1..71ba0273 100644 --- a/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts +++ b/packages/delegator-e2e/test/actions/erc7710sendUserOperationWithDelegation.test.ts @@ -119,7 +119,6 @@ test('maincase: Bob redeems the delegation in order to call increment() on the c }, ], ...gasPrice, - dependencies: [{ factory, factoryData }], }); const receipt = await bundlerClient.waitForUserOperationReceipt({