From e5b835ca29858a91d338151af29aff31c85a4847 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 13:41:35 +1300 Subject: [PATCH 01/11] feat: add CaveatType enum and integrate into core caveat builder --- .../src/caveatBuilder/caveatType.ts | 46 +++++++++++++++++++ .../src/caveatBuilder/coreCaveatBuilder.ts | 5 +- .../src/caveatBuilder/index.ts | 1 + 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts new file mode 100644 index 00000000..27197115 --- /dev/null +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts @@ -0,0 +1,46 @@ +/** + * Caveat types for enforcer functions used in delegations. + * Can be used when defining caveats either via CaveatBuilder.addCaveat + * or in the caveats array in createDelegation. + */ +export enum CaveatType { + AllowedMethods = 'allowedMethods', + AllowedTargets = 'allowedTargets', + Deployed = 'deployed', + AllowedCalldata = 'allowedCalldata', + Erc20BalanceChange = 'erc20BalanceChange', + Erc721BalanceChange = 'erc721BalanceChange', + Erc1155BalanceChange = 'erc1155BalanceChange', + ValueLte = 'valueLte', + LimitedCalls = 'limitedCalls', + Id = 'id', + Nonce = 'nonce', + Timestamp = 'timestamp', + BlockNumber = 'blockNumber', + Erc20TransferAmount = 'erc20TransferAmount', + Erc20Streaming = 'erc20Streaming', + NativeTokenStreaming = 'nativeTokenStreaming', + Erc721Transfer = 'erc721Transfer', + NativeTokenTransferAmount = 'nativeTokenTransferAmount', + NativeBalanceChange = 'nativeBalanceChange', + Redeemer = 'redeemer', + NativeTokenPayment = 'nativeTokenPayment', + ArgsEqualityCheck = 'argsEqualityCheck', + SpecificActionERC20TransferBatch = 'specificActionERC20TransferBatch', + Erc20PeriodTransfer = 'erc20PeriodTransfer', + NativeTokenPeriodTransfer = 'nativeTokenPeriodTransfer', + ExactCalldataBatch = 'exactCalldataBatch', + ExactCalldata = 'exactCalldata', + ExactExecution = 'exactExecution', + ExactExecutionBatch = 'exactExecutionBatch', + MultiTokenPeriod = 'multiTokenPeriod', + OwnershipTransfer = 'ownershipTransfer', +} + +/** + * Represents a caveat type that can be either the enum value or its string representation. + * This allows both forms: + * - CaveatType.AllowedMethods (enum) + * - 'allowedMethods' (string literal) + */ +export type CaveatTypeParam = CaveatType | `${CaveatType}`; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 963e2ce6..e6c1a868 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -1,4 +1,4 @@ -import type { SmartAccountsEnvironment } from '../types'; +import type { Caveat, SmartAccountsEnvironment } from '../types'; import { allowedCalldata, allowedCalldataBuilder, @@ -12,6 +12,7 @@ import { import { blockNumber, blockNumberBuilder } from './blockNumberBuilder'; import type { CaveatBuilderConfig } from './caveatBuilder'; import { CaveatBuilder } from './caveatBuilder'; +import type { CaveatTypeParam } from './caveatType'; import { deployed, deployedBuilder } from './deployedBuilder'; import { erc1155BalanceChange, @@ -120,7 +121,7 @@ type CoreCaveatMap = { exactExecutionBatch: typeof exactExecutionBatchBuilder; multiTokenPeriod: typeof multiTokenPeriodBuilder; ownershipTransfer: typeof ownershipTransferBuilder; -}; +} & Record Caveat>; /** * A caveat builder type that includes all core caveat types pre-configured. diff --git a/packages/smart-accounts-kit/src/caveatBuilder/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/index.ts index 62a11b9c..ac177ccb 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/index.ts @@ -12,3 +12,4 @@ export { CaveatBuilder } from './caveatBuilder'; export type { Caveats } from './resolveCaveats'; export { resolveCaveats } from './resolveCaveats'; +export { CaveatType } from './caveatType'; From 9ebadea1dec87edacdf6d88542965677650bb8c9 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 13:49:10 +1300 Subject: [PATCH 02/11] feat: reintroduce CaveatType enum and update core caveat builder integration --- .../src/caveatBuilder/caveatType.ts | 46 ------------------ .../src/caveatBuilder/coreCaveatBuilder.ts | 4 +- .../src/caveatBuilder/index.ts | 1 - packages/smart-accounts-kit/src/constants.ts | 47 +++++++++++++++++++ packages/smart-accounts-kit/src/index.ts | 7 ++- 5 files changed, 55 insertions(+), 50 deletions(-) delete mode 100644 packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts deleted file mode 100644 index 27197115..00000000 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts +++ /dev/null @@ -1,46 +0,0 @@ -/** - * Caveat types for enforcer functions used in delegations. - * Can be used when defining caveats either via CaveatBuilder.addCaveat - * or in the caveats array in createDelegation. - */ -export enum CaveatType { - AllowedMethods = 'allowedMethods', - AllowedTargets = 'allowedTargets', - Deployed = 'deployed', - AllowedCalldata = 'allowedCalldata', - Erc20BalanceChange = 'erc20BalanceChange', - Erc721BalanceChange = 'erc721BalanceChange', - Erc1155BalanceChange = 'erc1155BalanceChange', - ValueLte = 'valueLte', - LimitedCalls = 'limitedCalls', - Id = 'id', - Nonce = 'nonce', - Timestamp = 'timestamp', - BlockNumber = 'blockNumber', - Erc20TransferAmount = 'erc20TransferAmount', - Erc20Streaming = 'erc20Streaming', - NativeTokenStreaming = 'nativeTokenStreaming', - Erc721Transfer = 'erc721Transfer', - NativeTokenTransferAmount = 'nativeTokenTransferAmount', - NativeBalanceChange = 'nativeBalanceChange', - Redeemer = 'redeemer', - NativeTokenPayment = 'nativeTokenPayment', - ArgsEqualityCheck = 'argsEqualityCheck', - SpecificActionERC20TransferBatch = 'specificActionERC20TransferBatch', - Erc20PeriodTransfer = 'erc20PeriodTransfer', - NativeTokenPeriodTransfer = 'nativeTokenPeriodTransfer', - ExactCalldataBatch = 'exactCalldataBatch', - ExactCalldata = 'exactCalldata', - ExactExecution = 'exactExecution', - ExactExecutionBatch = 'exactExecutionBatch', - MultiTokenPeriod = 'multiTokenPeriod', - OwnershipTransfer = 'ownershipTransfer', -} - -/** - * Represents a caveat type that can be either the enum value or its string representation. - * This allows both forms: - * - CaveatType.AllowedMethods (enum) - * - 'allowedMethods' (string literal) - */ -export type CaveatTypeParam = CaveatType | `${CaveatType}`; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index e6c1a868..bea56f91 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -12,7 +12,6 @@ import { import { blockNumber, blockNumberBuilder } from './blockNumberBuilder'; import type { CaveatBuilderConfig } from './caveatBuilder'; import { CaveatBuilder } from './caveatBuilder'; -import type { CaveatTypeParam } from './caveatType'; import { deployed, deployedBuilder } from './deployedBuilder'; import { erc1155BalanceChange, @@ -84,6 +83,7 @@ import { } from './specificActionERC20TransferBatchBuilder'; import { timestamp, timestampBuilder } from './timestampBuilder'; import { valueLte, valueLteBuilder } from './valueLteBuilder'; +import type { CaveatType } from 'src/constants'; // While we could derive CoreCaveatMap from the createCaveatBuilder function, // doing so would significantly complicate type resolution. By explicitly @@ -121,7 +121,7 @@ type CoreCaveatMap = { exactExecutionBatch: typeof exactExecutionBatchBuilder; multiTokenPeriod: typeof multiTokenPeriodBuilder; ownershipTransfer: typeof ownershipTransferBuilder; -} & Record Caveat>; +} & Record Caveat>; /** * A caveat builder type that includes all core caveat types pre-configured. diff --git a/packages/smart-accounts-kit/src/caveatBuilder/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/index.ts index ac177ccb..62a11b9c 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/index.ts @@ -12,4 +12,3 @@ export { CaveatBuilder } from './caveatBuilder'; export type { Caveats } from './resolveCaveats'; export { resolveCaveats } from './resolveCaveats'; -export { CaveatType } from './caveatType'; diff --git a/packages/smart-accounts-kit/src/constants.ts b/packages/smart-accounts-kit/src/constants.ts index 11e9a2c9..85a7236a 100644 --- a/packages/smart-accounts-kit/src/constants.ts +++ b/packages/smart-accounts-kit/src/constants.ts @@ -53,3 +53,50 @@ export enum ScopeType { OwnershipTransfer = 'ownershipTransfer', FunctionCall = 'functionCall', } + +/** + * Caveat types for enforcer functions used in delegations. + * Can be used when defining caveats either via CaveatBuilder.addCaveat + * or in the caveats array in createDelegation. + */ +export enum CaveatType { + AllowedMethods = 'allowedMethods', + AllowedTargets = 'allowedTargets', + Deployed = 'deployed', + AllowedCalldata = 'allowedCalldata', + Erc20BalanceChange = 'erc20BalanceChange', + Erc721BalanceChange = 'erc721BalanceChange', + Erc1155BalanceChange = 'erc1155BalanceChange', + ValueLte = 'valueLte', + LimitedCalls = 'limitedCalls', + Id = 'id', + Nonce = 'nonce', + Timestamp = 'timestamp', + BlockNumber = 'blockNumber', + Erc20TransferAmount = 'erc20TransferAmount', + Erc20Streaming = 'erc20Streaming', + NativeTokenStreaming = 'nativeTokenStreaming', + Erc721Transfer = 'erc721Transfer', + NativeTokenTransferAmount = 'nativeTokenTransferAmount', + NativeBalanceChange = 'nativeBalanceChange', + Redeemer = 'redeemer', + NativeTokenPayment = 'nativeTokenPayment', + ArgsEqualityCheck = 'argsEqualityCheck', + SpecificActionERC20TransferBatch = 'specificActionERC20TransferBatch', + Erc20PeriodTransfer = 'erc20PeriodTransfer', + NativeTokenPeriodTransfer = 'nativeTokenPeriodTransfer', + ExactCalldataBatch = 'exactCalldataBatch', + ExactCalldata = 'exactCalldata', + ExactExecution = 'exactExecution', + ExactExecutionBatch = 'exactExecutionBatch', + MultiTokenPeriod = 'multiTokenPeriod', + OwnershipTransfer = 'ownershipTransfer', +} + +/** + * Represents a caveat type that can be either the enum value or its string representation. + * This allows both forms: + * - CaveatType.AllowedMethods (enum) + * - 'allowedMethods' (string literal) + */ +export type CaveatTypeParam = CaveatType | `${CaveatType}`; diff --git a/packages/smart-accounts-kit/src/index.ts b/packages/smart-accounts-kit/src/index.ts index 18aeab8c..5ac166f5 100644 --- a/packages/smart-accounts-kit/src/index.ts +++ b/packages/smart-accounts-kit/src/index.ts @@ -37,7 +37,12 @@ export { getSmartAccountsEnvironment, } from './smartAccountsEnvironment'; -export { Implementation, TransferWindow, ScopeType } from './constants'; +export { + Implementation, + TransferWindow, + ScopeType, + CaveatType, +} from './constants'; export { createExecution, ExecutionMode } from './executions'; From b98c9438a446e533ac7c36dbaf3b57046b7e3f88 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 13:53:51 +1300 Subject: [PATCH 03/11] feat: remove CaveatTypeParam type definition for simplification --- packages/smart-accounts-kit/src/constants.ts | 8 -------- 1 file changed, 8 deletions(-) diff --git a/packages/smart-accounts-kit/src/constants.ts b/packages/smart-accounts-kit/src/constants.ts index 85a7236a..43a66e8c 100644 --- a/packages/smart-accounts-kit/src/constants.ts +++ b/packages/smart-accounts-kit/src/constants.ts @@ -92,11 +92,3 @@ export enum CaveatType { MultiTokenPeriod = 'multiTokenPeriod', OwnershipTransfer = 'ownershipTransfer', } - -/** - * Represents a caveat type that can be either the enum value or its string representation. - * This allows both forms: - * - CaveatType.AllowedMethods (enum) - * - 'allowedMethods' (string literal) - */ -export type CaveatTypeParam = CaveatType | `${CaveatType}`; From ff0a32327d1e08fb64dd5cbdd42d2d29a01caa48 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 14:36:28 +1300 Subject: [PATCH 04/11] feat: refactor CoreCaveatMap type definition for improved type resolution --- .../src/caveatBuilder/coreCaveatBuilder.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index bea56f91..ac27e0e9 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -89,7 +89,7 @@ import type { CaveatType } from 'src/constants'; // doing so would significantly complicate type resolution. By explicitly // declaring the return type of createCaveatBuilder, we ensure the caveat // map remains synchronized with the actual implementation. -type CoreCaveatMap = { +type _CoreCaveatMap = { allowedMethods: typeof allowedMethodsBuilder; allowedTargets: typeof allowedTargetsBuilder; deployed: typeof deployedBuilder; @@ -121,7 +121,15 @@ type CoreCaveatMap = { exactExecutionBatch: typeof exactExecutionBatchBuilder; multiTokenPeriod: typeof multiTokenPeriodBuilder; ownershipTransfer: typeof ownershipTransferBuilder; -} & Record Caveat>; +}; + +type CaveatMapLookup = Key extends keyof CoreCaveatMap + ? CoreCaveatMap[Key] + : (...args: any[]) => Caveat; + +type CoreCaveatMap = _CoreCaveatMap & { + [K in CaveatType as `${K}`]: CaveatMapLookup<`${K}`>; +}; /** * A caveat builder type that includes all core caveat types pre-configured. From 5e0300cbe2558e68cf01f749a73667a9e6c0f97a Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 14:44:03 +1300 Subject: [PATCH 05/11] fix: update import path for CaveatType in coreCaveatBuilder --- .../smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index ac27e0e9..95bbd027 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -83,7 +83,7 @@ import { } from './specificActionERC20TransferBatchBuilder'; import { timestamp, timestampBuilder } from './timestampBuilder'; import { valueLte, valueLteBuilder } from './valueLteBuilder'; -import type { CaveatType } from 'src/constants'; +import type { CaveatType } from '../constants'; // While we could derive CoreCaveatMap from the createCaveatBuilder function, // doing so would significantly complicate type resolution. By explicitly From 296bb5e3fe31fb0d7705f4990ce823a0d9c0bf3b Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 14:45:26 +1300 Subject: [PATCH 06/11] feat: update CaveatMapLookup to use _CoreCaveatMap for improved type safety --- .../smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 95bbd027..5153d681 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -123,8 +123,8 @@ type _CoreCaveatMap = { ownershipTransfer: typeof ownershipTransferBuilder; }; -type CaveatMapLookup = Key extends keyof CoreCaveatMap - ? CoreCaveatMap[Key] +type CaveatMapLookup = Key extends keyof _CoreCaveatMap + ? _CoreCaveatMap[Key] : (...args: any[]) => Caveat; type CoreCaveatMap = _CoreCaveatMap & { From a36892638532ae2abc614f5a9798617a590fc17c Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 15:24:24 +1300 Subject: [PATCH 07/11] feat: simplify CaveatMapLookup type definition for clarity --- .../smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 5153d681..832788bb 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -123,9 +123,7 @@ type _CoreCaveatMap = { ownershipTransfer: typeof ownershipTransferBuilder; }; -type CaveatMapLookup = Key extends keyof _CoreCaveatMap - ? _CoreCaveatMap[Key] - : (...args: any[]) => Caveat; +type CaveatMapLookup = _CoreCaveatMap[Key]; type CoreCaveatMap = _CoreCaveatMap & { [K in CaveatType as `${K}`]: CaveatMapLookup<`${K}`>; From dc1e824531adc1d7d325615bba9ba6fd8f2a3978 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 15:39:47 +1300 Subject: [PATCH 08/11] feat: remove unused import of Caveat from coreCaveatBuilder for cleaner code --- .../smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 832788bb..b05ae7e8 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -1,4 +1,4 @@ -import type { Caveat, SmartAccountsEnvironment } from '../types'; +import type { SmartAccountsEnvironment } from '../types'; import { allowedCalldata, allowedCalldataBuilder, From 9cc0edbebf51f77a93ebe159be3c26b84afe6903 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 16:00:13 +1300 Subject: [PATCH 09/11] feat: update CoreCaveatMap to directly reference _CoreCaveatMap for improved type clarity --- .../smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index b05ae7e8..c11e5563 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -123,10 +123,8 @@ type _CoreCaveatMap = { ownershipTransfer: typeof ownershipTransferBuilder; }; -type CaveatMapLookup = _CoreCaveatMap[Key]; - type CoreCaveatMap = _CoreCaveatMap & { - [K in CaveatType as `${K}`]: CaveatMapLookup<`${K}`>; + [K in CaveatType as `${K}`]: _CoreCaveatMap[`${K}`]; }; /** From 99595959bfd26ecb410b743999c37f226066d4db Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 16:12:38 +1300 Subject: [PATCH 10/11] feat: add tests for caveat builders using CaveatType enum for improved coverage --- .../caveatBuilder/createCaveatBuilder.test.ts | 433 ++++++++++++++++++ 1 file changed, 433 insertions(+) diff --git a/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts index 7006e38f..a37816e2 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts @@ -4,6 +4,7 @@ import { expect, describe, it } from 'vitest'; import { createCaveatBuilder, CaveatBuilder } from '../../src/caveatBuilder'; import { BalanceChangeType } from '../../src/caveatBuilder/types'; +import { CaveatType } from '../../src/constants'; import type { SmartAccountsEnvironment } from '../../src/types'; import { randomAddress, randomBytes } from '../utils'; @@ -489,4 +490,436 @@ describe('createCaveatBuilder()', () => { expect(isAddress(caveat.enforcer)).to.equal(true); }); + + describe('caveat builders using CaveatType enum', () => { + it('should add allowedMethods caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const selectors = [randomBytes(4), randomBytes(4)]; + + const caveats = builder + .addCaveat(CaveatType.AllowedMethods, { selectors }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.AllowedMethodsEnforcer, + terms: concat(selectors), + args: '0x00', + }, + ]); + + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add allowedTargets caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const targets: [Address, Address] = [randomAddress(), randomAddress()]; + + const caveats = builder + .addCaveat(CaveatType.AllowedTargets, { targets }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.AllowedTargetsEnforcer, + terms: targets[0] + targets[1]?.slice(2), + args: '0x00', + }, + ]); + + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add deployed caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const contractAddress = randomAddress(); + const salt = randomBytes(32); + const bytecode = randomBytes(256); + + const caveats = builder + .addCaveat(CaveatType.Deployed, { contractAddress, salt, bytecode }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.DeployedEnforcer, + terms: concat([contractAddress, pad(salt, { size: 32 }), bytecode]), + args: '0x00', + }, + ]); + + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add allowedCalldata caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const value = randomBytes(128); + const startIndex = Math.floor(Math.random() * 2 ** 32); + + const caveats = builder + .addCaveat(CaveatType.AllowedCalldata, { startIndex, value }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.AllowedCalldataEnforcer, + terms: concat([toHex(startIndex, { size: 32 }), value]), + args: '0x00', + }, + ]); + + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add erc20BalanceChange caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const tokenAddress = randomAddress(); + const recipient = randomAddress(); + const balance = BigInt( + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER), + ); + + const caveats = builder + .addCaveat(CaveatType.Erc20BalanceChange, { + tokenAddress, + recipient, + balance, + changeType: BalanceChangeType.Increase, + }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ERC20BalanceChangeEnforcer, + terms: encodePacked( + ['uint8', 'address', 'address', 'uint256'], + [BalanceChangeType.Increase, tokenAddress, recipient, balance], + ), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add valueLte caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const maxValue = BigInt( + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER), + ); + + const caveats = builder + .addCaveat(CaveatType.ValueLte, { maxValue }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ValueLteEnforcer, + terms: toHex(maxValue, { size: 32 }), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add limitedCalls caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const limit = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + + const caveats = builder + .addCaveat(CaveatType.LimitedCalls, { limit }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.LimitedCallsEnforcer, + terms: toHex(limit, { size: 32 }), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add id caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const idValue = BigInt(Math.floor(Math.random() * 2 ** 32)); + + const caveats = builder.addCaveat(CaveatType.Id, { id: idValue }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.IdEnforcer, + terms: toHex(idValue, { size: 32 }), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add nonce caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const nonce = randomBytes(16); + + const caveats = builder.addCaveat(CaveatType.Nonce, { nonce }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.NonceEnforcer, + terms: pad(nonce, { size: 32 }), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add timestamp caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const afterThreshold = 1000; + const beforeThreshold = 2000; + + const caveats = builder + .addCaveat(CaveatType.Timestamp, { + afterThreshold, + beforeThreshold, + }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.TimestampEnforcer, + terms: concat([ + toHex(afterThreshold, { size: 16 }), + toHex(beforeThreshold, { size: 16 }), + ]), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add blockNumber caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const afterThreshold = 1000n; + const beforeThreshold = 2000n; + + const caveats = builder + .addCaveat(CaveatType.BlockNumber, { + afterThreshold, + beforeThreshold, + }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.BlockNumberEnforcer, + terms: concat([ + toHex(afterThreshold, { size: 16 }), + toHex(beforeThreshold, { size: 16 }), + ]), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add nativeTokenTransferAmount caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const maxAmount = 1000000000000000000n; // 1 ETH in wei + + const caveats = builder + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: + environment.caveatEnforcers.NativeTokenTransferAmountEnforcer, + terms: toHex(maxAmount, { size: 32 }), + args: '0x00', + }, + ]); + + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add nativeBalanceChange caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const recipient = randomAddress(); + const minBalance = 500000000000000000n; // 0.5 ETH in wei + + const caveats = builder + .addCaveat(CaveatType.NativeBalanceChange, { + recipient, + balance: minBalance, + changeType: BalanceChangeType.Increase, + }) + .build(); + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.NativeBalanceChangeEnforcer, + terms: encodePacked( + ['uint8', 'address', 'uint256'], + [BalanceChangeType.Increase, recipient, minBalance], + ), + args: '0x00', + }, + ]); + + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add nativeTokenPayment caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const amount = 1000000000000000000n; // 1 ETH in wei + const recipient = randomAddress('lowercase'); + + const caveats = builder + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.NativeTokenPaymentEnforcer, + terms: concat([recipient, toHex(amount, { size: 32 })]), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add erc20TransferAmount caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const tokenAddress = randomAddress(); + const maxAmount = 2000n; + + const caveats = builder + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress, maxAmount }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ERC20TransferAmountEnforcer, + terms: concat([tokenAddress, toHex(maxAmount, { size: 32 })]), + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add redeemer caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const redeemerAddress = randomAddress(); + + const caveats = builder + .addCaveat(CaveatType.Redeemer, { redeemers: [redeemerAddress] }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.RedeemerEnforcer, + terms: redeemerAddress, + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + + it('should add argsEqualityCheck caveat using CaveatType enum', () => { + const builder = createCaveatBuilder(environment); + const args = '0x1234567890'; + + const caveats = builder + .addCaveat(CaveatType.ArgsEqualityCheck, { args }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ArgsEqualityCheckEnforcer, + terms: args, + args: '0x00', + }, + ]); + const caveat = caveats[0]; + if (!caveat) { + throw new Error('caveat is not set'); + } + + expect(isAddress(caveat.enforcer)).to.equal(true); + }); + }); }); From 5327b00a1a819d687f83f40abc0c96e52c49afda Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 5 Mar 2026 17:24:46 +1300 Subject: [PATCH 11/11] feat: rename _CoreCaveatMap to CoreCaveatMapByString for clarity and update CoreCaveatMap type definition --- .../src/caveatBuilder/coreCaveatBuilder.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index c11e5563..36f05ba9 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -89,7 +89,7 @@ import type { CaveatType } from '../constants'; // doing so would significantly complicate type resolution. By explicitly // declaring the return type of createCaveatBuilder, we ensure the caveat // map remains synchronized with the actual implementation. -type _CoreCaveatMap = { +type CoreCaveatMapByString = { allowedMethods: typeof allowedMethodsBuilder; allowedTargets: typeof allowedTargetsBuilder; deployed: typeof deployedBuilder; @@ -123,8 +123,8 @@ type _CoreCaveatMap = { ownershipTransfer: typeof ownershipTransferBuilder; }; -type CoreCaveatMap = _CoreCaveatMap & { - [K in CaveatType as `${K}`]: _CoreCaveatMap[`${K}`]; +type CoreCaveatMap = CoreCaveatMapByString & { + [K in CaveatType as `${K}`]: CoreCaveatMapByString[`${K}`]; }; /**