From 09eb157a02a4f561cd7d0df05b9ae3d8a0f974ad Mon Sep 17 00:00:00 2001 From: Jeff Smale <6363749+jeffsmale90@users.noreply.github.com> Date: Tue, 16 Sep 2025 16:58:31 +1200 Subject: [PATCH 1/3] Fix MultiTokenPeriodBuilder to accept a config object, because array parameter breaks declarative interface --- .../caveatBuilder/multiTokenPeriodBuilder.ts | 27 ++++--- .../multiTokenPeriodBuilder.test.ts | 58 ++++++++------- .../test/caveats/caveatUtils.test.ts | 74 ++++++++++--------- .../test/caveats/multiTokenPeriod.test.ts | 4 +- 4 files changed, 89 insertions(+), 74 deletions(-) diff --git a/packages/delegation-toolkit/src/caveatBuilder/multiTokenPeriodBuilder.ts b/packages/delegation-toolkit/src/caveatBuilder/multiTokenPeriodBuilder.ts index 0ae67d0e..031f8411 100644 --- a/packages/delegation-toolkit/src/caveatBuilder/multiTokenPeriodBuilder.ts +++ b/packages/delegation-toolkit/src/caveatBuilder/multiTokenPeriodBuilder.ts @@ -22,7 +22,9 @@ export type TokenPeriodConfig = { startDate: number; }; -export type MultiTokenPeriodBuilderConfig = TokenPeriodConfig[]; +export type MultiTokenPeriodBuilderConfig = { + tokenConfigs: TokenPeriodConfig[]; +}; export const multiTokenPeriod = 'multiTokenPeriod'; @@ -32,27 +34,30 @@ export const multiTokenPeriod = 'multiTokenPeriod'; * Each token can have its own period amount, duration, and start date. * * @param environment - The DeleGator environment. - * @param configs - The configurations for the MultiTokenPeriodBuilder. + * @param config - The configuration for the MultiTokenPeriodBuilder. + * @param config.tokenConfigs - The token configurations for the MultiTokenPeriodBuilder. * @returns The caveat object for the MultiTokenPeriodEnforcer. */ export const multiTokenPeriodBuilder = ( environment: DeleGatorEnvironment, - configs: MultiTokenPeriodBuilderConfig, + config: MultiTokenPeriodBuilderConfig, ): Caveat => { - if (!configs || configs.length === 0) { - throw new Error('MultiTokenPeriodBuilder: configs array cannot be empty'); + if (!config?.tokenConfigs || config.tokenConfigs.length === 0) { + throw new Error( + 'MultiTokenPeriodBuilder: tokenConfigs array cannot be empty', + ); } - configs.forEach((config) => { - if (!isAddress(config.token)) { - throw new Error(`Invalid token address: ${String(config.token)}`); + config.tokenConfigs.forEach((tokenConfig) => { + if (!isAddress(tokenConfig.token)) { + throw new Error(`Invalid token address: ${String(tokenConfig.token)}`); } - if (config.periodAmount <= 0) { + if (tokenConfig.periodAmount <= 0) { throw new Error('Invalid period amount: must be greater than 0'); } - if (config.periodDuration <= 0) { + if (tokenConfig.periodDuration <= 0) { throw new Error('Invalid period duration: must be greater than 0'); } }); @@ -62,7 +67,7 @@ export const multiTokenPeriodBuilder = ( // - 32 bytes for periodAmount // - 32 bytes for periodDuration // - 32 bytes for startDate - const termsArray = configs.reduce( + const termsArray = config.tokenConfigs.reduce( (acc, { token, periodAmount, periodDuration, startDate }) => [ ...acc, pad(token, { size: 20 }), diff --git a/packages/delegation-toolkit/test/caveatBuilder/multiTokenPeriodBuilder.test.ts b/packages/delegation-toolkit/test/caveatBuilder/multiTokenPeriodBuilder.test.ts index 2190f71a..359b0076 100644 --- a/packages/delegation-toolkit/test/caveatBuilder/multiTokenPeriodBuilder.test.ts +++ b/packages/delegation-toolkit/test/caveatBuilder/multiTokenPeriodBuilder.test.ts @@ -1,5 +1,5 @@ import type { Hex } from 'viem'; -import { concat, size, slice, toHex } from 'viem'; +import { concat, pad, size, slice, toHex } from 'viem'; import { expect, describe, it } from 'vitest'; import type { TokenPeriodConfig } from '../../src/caveatBuilder/multiTokenPeriodBuilder'; @@ -38,11 +38,15 @@ describe('multiTokenPeriodBuilder', () => { }); it('should encode a single token configuration correctly', () => { - const result = multiTokenPeriodBuilder(mockEnvironment, [mockConfig]); + const result = multiTokenPeriodBuilder(mockEnvironment, { + tokenConfigs: [mockConfig], + }); expect(size(result.terms)).to.equal(LENGTH_PER_CONFIG); - expect(slice(result.terms, 0, 20)).to.equal(mockConfig.token); + expect(slice(result.terms, 0, 20)).to.equal( + pad(mockConfig.token, { size: 20 }), + ); expect(result.enforcer).to.equal( mockEnvironment.caveatEnforcers.MultiTokenPeriodEnforcer, ); @@ -50,10 +54,9 @@ describe('multiTokenPeriodBuilder', () => { }); it('should encode multiple token correctly', () => { - const result = multiTokenPeriodBuilder(mockEnvironment as any, [ - mockConfig, - secondMockConfig, - ]); + const result = multiTokenPeriodBuilder(mockEnvironment as any, { + tokenConfigs: [mockConfig, secondMockConfig], + }); expect(result.enforcer).to.equal( mockEnvironment.caveatEnforcers.MultiTokenPeriodEnforcer, @@ -61,11 +64,11 @@ describe('multiTokenPeriodBuilder', () => { expect(result.args).to.equal('0x'); expect(result.terms).to.equal( concat([ - mockConfig.token, + pad(mockConfig.token, { size: 20 }), toHex(mockConfig.periodAmount, { size: 32 }), toHex(mockConfig.periodDuration, { size: 32 }), toHex(mockConfig.startDate, { size: 32 }), - secondMockConfig.token, + pad(secondMockConfig.token, { size: 20 }), toHex(secondMockConfig.periodAmount, { size: 32 }), toHex(secondMockConfig.periodDuration, { size: 32 }), toHex(secondMockConfig.startDate, { size: 32 }), @@ -74,15 +77,15 @@ describe('multiTokenPeriodBuilder', () => { }); it('should throw error for empty configs array', () => { - expect(() => multiTokenPeriodBuilder(mockEnvironment as any, [])).to.throw( - 'MultiTokenPeriodBuilder: configs array cannot be empty', - ); + expect(() => + multiTokenPeriodBuilder(mockEnvironment as any, { tokenConfigs: [] }), + ).to.throw('MultiTokenPeriodBuilder: tokenConfigs array cannot be empty'); }); it('should encode numeric values correctly', () => { - const result = multiTokenPeriodBuilder(mockEnvironment as any, [ - mockConfig, - ]); + const result = multiTokenPeriodBuilder(mockEnvironment as any, { + tokenConfigs: [mockConfig], + }); expect(slice(result.terms, 20, 20 + 32)).to.equal( toHex(mockConfig.periodAmount, { size: 32 }), ); @@ -92,34 +95,33 @@ describe('multiTokenPeriodBuilder', () => { }); it('should validate terms length is multiple of 116 bytes', () => { - const result = multiTokenPeriodBuilder(mockEnvironment as any, [ - mockConfig, - secondMockConfig, - ]); + const result = multiTokenPeriodBuilder(mockEnvironment as any, { + tokenConfigs: [mockConfig, secondMockConfig], + }); expect(size(result.terms)).to.equal(LENGTH_PER_CONFIG * 2); }); it('should throw error for invalid token address', () => { expect(() => - multiTokenPeriodBuilder(mockEnvironment as any, [ - { ...mockConfig, token: 'invalid' as Hex }, - ]), + multiTokenPeriodBuilder(mockEnvironment as any, { + tokenConfigs: [{ ...mockConfig, token: 'invalid' as Hex }], + }), ).to.throw('Invalid token address: invalid'); }); it('should throw error for invalid period amount', () => { expect(() => - multiTokenPeriodBuilder(mockEnvironment as any, [ - { ...mockConfig, periodAmount: 0n }, - ]), + multiTokenPeriodBuilder(mockEnvironment as any, { + tokenConfigs: [{ ...mockConfig, periodAmount: 0n }], + }), ).to.throw('Invalid period amount: must be greater than 0'); }); it('should throw error for invalid period duration', () => { expect(() => - multiTokenPeriodBuilder(mockEnvironment as any, [ - { ...mockConfig, periodDuration: 0 }, - ]), + multiTokenPeriodBuilder(mockEnvironment as any, { + tokenConfigs: [{ ...mockConfig, periodDuration: 0 }], + }), ).to.throw('Invalid period duration: must be greater than 0'); }); }); diff --git a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts index 32072533..90b749d7 100644 --- a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts +++ b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts @@ -623,16 +623,18 @@ describe('MultiTokenPeriodEnforcer', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', [ - { - token: erc20TokenAddress, - periodAmount, - periodDuration, - startDate: currentTime, - }, - ]) + .addCaveat('multiTokenPeriod', { + tokenConfigs: [ + { + token: erc20TokenAddress, + periodAmount, + periodDuration, + startDate: currentTime, + }, + ], + }) .build(), - salt: '0x1' as Hex, + salt: '0x1' as const, signature: '0x1', }; @@ -719,14 +721,16 @@ describe('MultiTokenPeriodEnforcer', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', [ - { - token: erc20TokenAddress, - periodAmount, - periodDuration, - startDate: futureStartDate, - }, - ]) + .addCaveat('multiTokenPeriod', { + tokenConfigs: [ + { + token: erc20TokenAddress, + periodAmount, + periodDuration, + startDate: futureStartDate, + }, + ], + } as any) .build(), salt: '0x1' as Hex, signature: '0x1', @@ -805,14 +809,16 @@ describe('MultiTokenPeriodEnforcer', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', [ - { - token: erc20TokenAddress, - periodAmount, - periodDuration, - startDate, - }, - ]) + .addCaveat('multiTokenPeriod', { + tokenConfigs: [ + { + token: erc20TokenAddress, + periodAmount, + periodDuration, + startDate, + }, + ], + } as any) .build(), salt: '0x1' as Hex, signature: '0x1', @@ -1501,14 +1507,16 @@ describe('Individual action functions vs client extension methods', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', [ - { - token: erc20TokenAddress, - periodAmount, - periodDuration, - startDate: currentTime, - }, - ]) + .addCaveat('multiTokenPeriod', { + tokenConfigs: [ + { + token: erc20TokenAddress, + periodAmount, + periodDuration, + startDate: currentTime, + }, + ], + } as any) .build(), salt: '0x1' as Hex, signature: '0x1', diff --git a/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts b/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts index 73621559..acaa9527 100644 --- a/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts +++ b/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts @@ -127,7 +127,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('multiTokenPeriod', configs) + .addCaveat('multiTokenPeriod', { tokenConfigs: configs }) .build(), signature: '0x', }; @@ -215,7 +215,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('multiTokenPeriod', configs) + .addCaveat('multiTokenPeriod', { tokenConfigs: configs }) .build(), signature: '0x', }; From b79b5050af2651b8a4e155fe298d78f8e86b7b63 Mon Sep 17 00:00:00 2001 From: Jeff Smale <6363749+jeffsmale90@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:15:15 +1200 Subject: [PATCH 2/3] Update dependencies --- packages/delegation-deployments/package.json | 1 + packages/delegation-toolkit/package.json | 1 + packages/delegator-e2e/package.json | 2 +- yarn.lock | 12 +++++++----- 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/packages/delegation-deployments/package.json b/packages/delegation-deployments/package.json index 9f049b49..1a4bbd35 100644 --- a/packages/delegation-deployments/package.json +++ b/packages/delegation-deployments/package.json @@ -55,6 +55,7 @@ "@metamask/auto-changelog": "^5.0.2", "compare-versions": "^6.1.1", "tsup": "^8.5.0", + "tsx": "^4.20.5", "typescript": "5.0.4" } } diff --git a/packages/delegation-toolkit/package.json b/packages/delegation-toolkit/package.json index 93bd0df1..a57f747e 100644 --- a/packages/delegation-toolkit/package.json +++ b/packages/delegation-toolkit/package.json @@ -140,6 +140,7 @@ "ts-node": "^10.9.2", "tsconfig-paths": "^4.2.0", "tsup": "^8.5.0", + "tsx": "^4.20.5", "typescript": "5.0.4", "viem": "2.31.4", "vitest": "^3.2.4" diff --git a/packages/delegator-e2e/package.json b/packages/delegator-e2e/package.json index cb436e3b..c4721a3c 100644 --- a/packages/delegator-e2e/package.json +++ b/packages/delegator-e2e/package.json @@ -8,7 +8,7 @@ "@metamask/delegation-toolkit": "workspace:*", "dotenv": "^16.3.1", "permissionless": "^0.1.44", - "tsx": "^4.19.3", + "tsx": "4.20.5", "vitest": "^3.2.4" }, "scripts": { diff --git a/yarn.lock b/yarn.lock index f7b09e2d..1df9cefb 100644 --- a/yarn.lock +++ b/yarn.lock @@ -601,6 +601,7 @@ __metadata: "@metamask/auto-changelog": "npm:^5.0.2" compare-versions: "npm:^6.1.1" tsup: "npm:^8.5.0" + tsx: "npm:^4.20.5" typescript: "npm:5.0.4" languageName: unknown linkType: soft @@ -633,6 +634,7 @@ __metadata: ts-node: "npm:^10.9.2" tsconfig-paths: "npm:^4.2.0" tsup: "npm:^8.5.0" + tsx: "npm:^4.20.5" typescript: "npm:5.0.4" viem: "npm:2.31.4" vitest: "npm:^3.2.4" @@ -649,7 +651,7 @@ __metadata: "@metamask/delegation-toolkit": "workspace:*" dotenv: "npm:^16.3.1" permissionless: "npm:^0.1.44" - tsx: "npm:^4.19.3" + tsx: "npm:4.20.5" vitest: "npm:^3.2.4" peerDependencies: viem: ^2.31.4 @@ -6576,9 +6578,9 @@ __metadata: languageName: node linkType: hard -"tsx@npm:^4.19.3": - version: 4.19.3 - resolution: "tsx@npm:4.19.3" +"tsx@npm:4.20.5, tsx@npm:^4.20.5": + version: 4.20.5 + resolution: "tsx@npm:4.20.5" dependencies: esbuild: "npm:~0.25.0" fsevents: "npm:~2.3.3" @@ -6588,7 +6590,7 @@ __metadata: optional: true bin: tsx: dist/cli.mjs - checksum: 10/a7e7f41e5593b242772050abacf51908aa8a6d4d9ea6c29e80161eb557d664a0f4cc8d38d0c8c151fddb6c2e9e337af27ba0e269c9707ccd7eeff0e0ea7fcf98 + checksum: 10/161420678027c43d07b60b7b6b512cc67ff86ae3cca0641a19b0d3e742c5e262bca57034c4bff6d9346f9269e9ada24b6030e1d2bc890df5e1a9754865d3c08a languageName: node linkType: hard From 50e5de35e6ea8744b0340dfda6c9b9398e25c14d Mon Sep 17 00:00:00 2001 From: Jeff Smale <6363749+jeffsmale90@users.noreply.github.com> Date: Wed, 17 Sep 2025 12:17:15 +1200 Subject: [PATCH 3/3] Remove unnecessary 'as any' type assertions --- packages/delegator-e2e/test/caveats/caveatUtils.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts index 90b749d7..4fc99fc0 100644 --- a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts +++ b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts @@ -730,7 +730,7 @@ describe('MultiTokenPeriodEnforcer', () => { startDate: futureStartDate, }, ], - } as any) + }) .build(), salt: '0x1' as Hex, signature: '0x1', @@ -818,7 +818,7 @@ describe('MultiTokenPeriodEnforcer', () => { startDate, }, ], - } as any) + }) .build(), salt: '0x1' as Hex, signature: '0x1', @@ -1516,7 +1516,7 @@ describe('Individual action functions vs client extension methods', () => { startDate: currentTime, }, ], - } as any) + }) .build(), salt: '0x1' as Hex, signature: '0x1',