From 7e260fec7aa19c41fcdb3ddc5896fdd5260c141d Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Tue, 24 Feb 2026 19:16:28 +1300 Subject: [PATCH 01/18] feat: introduce CaveatType enum and normalize caveat type handling in builders --- .../src/caveatBuilder/caveatBuilder.ts | 15 ++++++- .../src/caveatBuilder/coreCaveatBuilder.ts | 22 ++++++++--- .../src/caveatBuilder/index.ts | 6 ++- .../src/caveatBuilder/resolveCaveats.ts | 11 +++++- packages/smart-accounts-kit/src/constants.ts | 39 +++++++++++++++++++ packages/smart-accounts-kit/src/index.ts | 7 +++- 6 files changed, 91 insertions(+), 9 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts index e9eda865..21f9d526 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts @@ -1,3 +1,4 @@ +import { CaveatType } from '../constants'; import type { Caveat, SmartAccountsEnvironment } from '../types'; type CaveatWithOptionalArgs = Omit & { @@ -7,6 +8,17 @@ type CaveatWithOptionalArgs = Omit & { const INSECURE_UNRESTRICTED_DELEGATION_ERROR_MESSAGE = 'No caveats found. If you definitely want to create an empty caveat collection, set `allowInsecureUnrestrictedDelegation` to `true`.'; +// Helper to normalize caveat type names to their enum representation +const normalizeCaveatName = ( + name: string | CaveatType, +): CaveatType | string => { + // If it's already a CaveatType enum value, return as-is + // Otherwise, return the string value + return Object.values(CaveatType).includes(name as CaveatType) + ? (name as CaveatType) + : name; +}; + type CaveatBuilderMap = { [key: string]: ( environment: SmartAccountsEnvironment, @@ -111,7 +123,8 @@ export class CaveatBuilder< return this; } - const name = nameOrCaveat; + const normalizedName = normalizeCaveatName(nameOrCaveat as string); + const name = normalizedName as TEnforcerName; const func = this.#enforcerBuilders[name]; if (typeof func === 'function') { diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 963e2ce6..8afba839 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -1,3 +1,4 @@ +import type { CaveatType } from '../constants'; import type { SmartAccountsEnvironment } from '../types'; import { allowedCalldata, @@ -129,6 +130,15 @@ type CoreCaveatMap = { */ export type CoreCaveatBuilder = CaveatBuilder; +// We want to allow the caveat `type` to be passed as either an enum reference, +// or the enum's string value. This generic accepts a union of caveat configs, and +// converts them to an identical union except the `type` parameter is converted +// to a union of `CaveatType.XXXX | `${CaveatType.XXXX}`. +export type ConvertCaveatConfigsToInputs = + T extends { type: infer TType extends string } + ? Omit & { type: TType | CaveatType } + : never; + type ExtractCaveatMapType> = TCaveatBuilder extends CaveatBuilder ? TCaveatMap : never; type ExtractedCoreMap = ExtractCaveatMapType; @@ -144,11 +154,13 @@ export type CaveatConfiguration< CaveatMap = ExtractCaveatMapType, > = CaveatMap extends Record any> - ? { - [TType in keyof CaveatMap]: { - type: TType; - } & Parameters[1]; - }[keyof CaveatMap] + ? ConvertCaveatConfigsToInputs< + { + [TType in keyof CaveatMap]: { + type: TType extends string ? TType : never; + } & Parameters[1]; + }[keyof CaveatMap] + > : never; export type CoreCaveatConfiguration = CaveatConfiguration; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/index.ts index 62a11b9c..1dd72c1c 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/index.ts @@ -1,7 +1,11 @@ // Core Caveat Builder export { createCaveatBuilder } from './coreCaveatBuilder'; -export type { CoreCaveatBuilder } from './coreCaveatBuilder'; +export type { + CoreCaveatBuilder, + CoreCaveatConfiguration, + ConvertCaveatConfigsToInputs, +} from './coreCaveatBuilder'; // Caveat Builder implementation export type { CaveatBuilderConfig } from './caveatBuilder'; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts index 0bbeadec..2871f571 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts @@ -1,3 +1,4 @@ +import { CaveatType } from '../constants'; import type { CaveatBuilder } from './caveatBuilder'; import type { CoreCaveatConfiguration } from './coreCaveatBuilder'; import { createCaveatBuilderFromScope, type ScopeConfig } from './scope'; @@ -5,6 +6,13 @@ import type { Caveat, SmartAccountsEnvironment } from '../types'; export type Caveats = CaveatBuilder | (Caveat | CoreCaveatConfiguration)[]; +// Helper to normalize caveat type to its enum representation +const normalizeCaveatType = (type: string | CaveatType): CaveatType => { + return Object.values(CaveatType).includes(type as CaveatType) + ? (type as CaveatType) + : (type as CaveatType); +}; + /** * Resolves the array of Caveat from a Caveats argument. * @@ -35,7 +43,8 @@ export const resolveCaveats = ({ try { if ('type' in caveat) { const { type, ...config } = caveat; - scopeCaveatBuilder.addCaveat(type, config); + const normalizedType = normalizeCaveatType(type as string); + scopeCaveatBuilder.addCaveat(normalizedType, config); } else { scopeCaveatBuilder.addCaveat(caveat); } diff --git a/packages/smart-accounts-kit/src/constants.ts b/packages/smart-accounts-kit/src/constants.ts index 11e9a2c9..43a66e8c 100644 --- a/packages/smart-accounts-kit/src/constants.ts +++ b/packages/smart-accounts-kit/src/constants.ts @@ -53,3 +53,42 @@ 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', +} 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 9368578e09f2b17c7a21bee54b9b6f5b944bf794 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 25 Feb 2026 09:41:04 +1300 Subject: [PATCH 02/18] feat: implement validateCaveatType function for robust caveat type validation --- .../src/caveatBuilder/caveatBuilder.ts | 21 ++++++------------- .../src/caveatBuilder/resolveCaveats.ts | 17 ++++++--------- packages/smart-accounts-kit/src/utils.ts | 20 ++++++++++++++++++ 3 files changed, 32 insertions(+), 26 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts index 21f9d526..20422bd7 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts @@ -1,5 +1,5 @@ -import { CaveatType } from '../constants'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { validateCaveatType } from '../utils'; type CaveatWithOptionalArgs = Omit & { args?: Caveat['args']; @@ -8,17 +8,6 @@ type CaveatWithOptionalArgs = Omit & { const INSECURE_UNRESTRICTED_DELEGATION_ERROR_MESSAGE = 'No caveats found. If you definitely want to create an empty caveat collection, set `allowInsecureUnrestrictedDelegation` to `true`.'; -// Helper to normalize caveat type names to their enum representation -const normalizeCaveatName = ( - name: string | CaveatType, -): CaveatType | string => { - // If it's already a CaveatType enum value, return as-is - // Otherwise, return the string value - return Object.values(CaveatType).includes(name as CaveatType) - ? (name as CaveatType) - : name; -}; - type CaveatBuilderMap = { [key: string]: ( environment: SmartAccountsEnvironment, @@ -123,8 +112,7 @@ export class CaveatBuilder< return this; } - const normalizedName = normalizeCaveatName(nameOrCaveat as string); - const name = normalizedName as TEnforcerName; + const name = validateCaveatType(nameOrCaveat as string) as TEnforcerName; const func = this.#enforcerBuilders[name]; if (typeof func === 'function') { @@ -134,7 +122,10 @@ export class CaveatBuilder< return this; } - throw new Error(`Function "${String(name)}" does not exist.`); + throw new Error( + `Enforcer for caveat type "${String(name)}" is not registered. ` + + 'This may indicate a misconfigured CaveatBuilder.', + ); } /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts index 2871f571..0c32411e 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts @@ -1,4 +1,4 @@ -import { CaveatType } from '../constants'; +import { validateCaveatType } from '../utils'; import type { CaveatBuilder } from './caveatBuilder'; import type { CoreCaveatConfiguration } from './coreCaveatBuilder'; import { createCaveatBuilderFromScope, type ScopeConfig } from './scope'; @@ -6,13 +6,6 @@ import type { Caveat, SmartAccountsEnvironment } from '../types'; export type Caveats = CaveatBuilder | (Caveat | CoreCaveatConfiguration)[]; -// Helper to normalize caveat type to its enum representation -const normalizeCaveatType = (type: string | CaveatType): CaveatType => { - return Object.values(CaveatType).includes(type as CaveatType) - ? (type as CaveatType) - : (type as CaveatType); -}; - /** * Resolves the array of Caveat from a Caveats argument. * @@ -39,17 +32,19 @@ export const resolveCaveats = ({ scopeCaveatBuilder.addCaveat(caveat); }); } else if (Array.isArray(caveats)) { - caveats.forEach((caveat) => { + caveats.forEach((caveat, index) => { try { if ('type' in caveat) { const { type, ...config } = caveat; - const normalizedType = normalizeCaveatType(type as string); + const normalizedType = validateCaveatType(type as string); scopeCaveatBuilder.addCaveat(normalizedType, config); } else { scopeCaveatBuilder.addCaveat(caveat); } } catch (error) { - throw new Error(`Invalid caveat: ${(error as Error).message}`); + throw new Error( + `Error processing caveats[${index}]: ${(error as Error).message}`, + ); } }); } diff --git a/packages/smart-accounts-kit/src/utils.ts b/packages/smart-accounts-kit/src/utils.ts index 14f07b91..6122f532 100644 --- a/packages/smart-accounts-kit/src/utils.ts +++ b/packages/smart-accounts-kit/src/utils.ts @@ -1,5 +1,25 @@ import { type Hex, isHex, toHex } from 'viem'; +import { CaveatType } from './constants'; + +/** + * Validates and returns a caveat type, throwing an error if invalid. + * Accepts both CaveatType enum values and their string representations. + * + * @param type - The caveat type to validate (either enum value or string). + * @returns The validated caveat type. + * @throws {Error} If the caveat type is not recognized. + */ +export function validateCaveatType(type: string | CaveatType): CaveatType { + const validTypes = Object.values(CaveatType); + if (!validTypes.includes(type as CaveatType)) { + throw new Error( + `Invalid caveat type: "${type}". Valid types are: ${validTypes.join(', ')}`, + ); + } + return type as CaveatType; +} + /** * Checks if two hexadecimal strings are equal, ignoring case sensitivity. * From 7f1699b5f1d98a57d8acb83b1249d620ede74d9b Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 25 Feb 2026 15:24:01 +1300 Subject: [PATCH 03/18] refactor: simplify caveat type validation and improve error messaging --- .../smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts | 8 ++------ packages/smart-accounts-kit/src/utils.ts | 7 ++++--- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts index 20422bd7..e9eda865 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts @@ -1,5 +1,4 @@ import type { Caveat, SmartAccountsEnvironment } from '../types'; -import { validateCaveatType } from '../utils'; type CaveatWithOptionalArgs = Omit & { args?: Caveat['args']; @@ -112,7 +111,7 @@ export class CaveatBuilder< return this; } - const name = validateCaveatType(nameOrCaveat as string) as TEnforcerName; + const name = nameOrCaveat; const func = this.#enforcerBuilders[name]; if (typeof func === 'function') { @@ -122,10 +121,7 @@ export class CaveatBuilder< return this; } - throw new Error( - `Enforcer for caveat type "${String(name)}" is not registered. ` + - 'This may indicate a misconfigured CaveatBuilder.', - ); + throw new Error(`Function "${String(name)}" does not exist.`); } /** diff --git a/packages/smart-accounts-kit/src/utils.ts b/packages/smart-accounts-kit/src/utils.ts index 6122f532..5f6603cd 100644 --- a/packages/smart-accounts-kit/src/utils.ts +++ b/packages/smart-accounts-kit/src/utils.ts @@ -3,12 +3,13 @@ import { type Hex, isHex, toHex } from 'viem'; import { CaveatType } from './constants'; /** - * Validates and returns a caveat type, throwing an error if invalid. - * Accepts both CaveatType enum values and their string representations. + * Validates that a caveat type is a recognized CaveatType enum value. + * Only validates against standard CaveatType enum values. + * Custom caveat types registered via CaveatBuilder.extend() bypass this validation. * * @param type - The caveat type to validate (either enum value or string). * @returns The validated caveat type. - * @throws {Error} If the caveat type is not recognized. + * @throws {Error} If the caveat type is not a recognized CaveatType enum value. */ export function validateCaveatType(type: string | CaveatType): CaveatType { const validTypes = Object.values(CaveatType); From d6c499f722ece6997e6d4074205d3bdb4856b1b1 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 25 Feb 2026 15:37:37 +1300 Subject: [PATCH 04/18] refactor: enhance type handling in ConvertCaveatConfigsToInputs for better TypeScript inference --- .../src/caveatBuilder/coreCaveatBuilder.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 8afba839..e7080de8 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -1,4 +1,3 @@ -import type { CaveatType } from '../constants'; import type { SmartAccountsEnvironment } from '../types'; import { allowedCalldata, @@ -134,9 +133,12 @@ export type CoreCaveatBuilder = CaveatBuilder; // or the enum's string value. This generic accepts a union of caveat configs, and // converts them to an identical union except the `type` parameter is converted // to a union of `CaveatType.XXXX | `${CaveatType.XXXX}`. +// We preserve the discriminated union pattern by using T['type'] instead of +// expanding to the full CaveatType enum, which ensures TypeScript can narrow +// by the type field. export type ConvertCaveatConfigsToInputs = - T extends { type: infer TType extends string } - ? Omit & { type: TType | CaveatType } + T extends { type: string } + ? Omit & { type: T['type'] | `${T['type']}` } : never; type ExtractCaveatMapType> = From ce1aa47820d11cd8ff4fc3a18e6f98b8e081b677 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 26 Feb 2026 20:49:22 +1300 Subject: [PATCH 05/18] - Change validateCaveatType to use 'asserts type is CaveatType' instead of returning a value, allowing TypeScript to narrow the type and eliminating the need for variable reassignment - Remove unnecessary 'as string' type assertion in resolveCaveats.ts - the type is already correctly inferred - Move CaveatType enum from constants.ts to coreCaveatBuilder.ts to better organize the caveat-specific type definition - Remove ConvertCaveatConfigsToInputs and CoreCaveatConfiguration from public exports - only expose CaveatType and essential builder types - Update imports across the codebase to reflect the new locations --- .../src/caveatBuilder/coreCaveatBuilder.ts | 39 ++ .../src/caveatBuilder/index.ts | 8 +- .../src/caveatBuilder/resolveCaveats.ts | 4 +- packages/smart-accounts-kit/src/constants.ts | 39 -- packages/smart-accounts-kit/src/index.ts | 9 +- packages/smart-accounts-kit/src/utils.ts | 8 +- .../test/caveatBuilder/caveatTypeEnum.test.ts | 446 ++++++++++++++++++ 7 files changed, 496 insertions(+), 57 deletions(-) create mode 100644 packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index e7080de8..398aa9dc 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -84,6 +84,45 @@ import { import { timestamp, timestampBuilder } from './timestampBuilder'; import { valueLte, valueLteBuilder } from './valueLteBuilder'; +/** + * 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', +} + // While we could derive CoreCaveatMap from the createCaveatBuilder function, // doing so would significantly complicate type resolution. By explicitly // declaring the return type of createCaveatBuilder, we ensure the caveat diff --git a/packages/smart-accounts-kit/src/caveatBuilder/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/index.ts index 1dd72c1c..ea3fdf7c 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/index.ts @@ -1,11 +1,7 @@ // Core Caveat Builder -export { createCaveatBuilder } from './coreCaveatBuilder'; +export { createCaveatBuilder, CaveatType } from './coreCaveatBuilder'; -export type { - CoreCaveatBuilder, - CoreCaveatConfiguration, - ConvertCaveatConfigsToInputs, -} from './coreCaveatBuilder'; +export type { CoreCaveatBuilder } from './coreCaveatBuilder'; // Caveat Builder implementation export type { CaveatBuilderConfig } from './caveatBuilder'; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts index 0c32411e..b7ca0df0 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts @@ -36,8 +36,8 @@ export const resolveCaveats = ({ try { if ('type' in caveat) { const { type, ...config } = caveat; - const normalizedType = validateCaveatType(type as string); - scopeCaveatBuilder.addCaveat(normalizedType, config); + validateCaveatType(type); + scopeCaveatBuilder.addCaveat(type, config); } else { scopeCaveatBuilder.addCaveat(caveat); } diff --git a/packages/smart-accounts-kit/src/constants.ts b/packages/smart-accounts-kit/src/constants.ts index 43a66e8c..11e9a2c9 100644 --- a/packages/smart-accounts-kit/src/constants.ts +++ b/packages/smart-accounts-kit/src/constants.ts @@ -53,42 +53,3 @@ 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', -} diff --git a/packages/smart-accounts-kit/src/index.ts b/packages/smart-accounts-kit/src/index.ts index 5ac166f5..f845464b 100644 --- a/packages/smart-accounts-kit/src/index.ts +++ b/packages/smart-accounts-kit/src/index.ts @@ -37,12 +37,7 @@ export { getSmartAccountsEnvironment, } from './smartAccountsEnvironment'; -export { - Implementation, - TransferWindow, - ScopeType, - CaveatType, -} from './constants'; +export { Implementation, TransferWindow, ScopeType } from './constants'; export { createExecution, ExecutionMode } from './executions'; @@ -52,6 +47,8 @@ export type { Caveats } from './caveatBuilder'; export { createCaveat } from './caveats'; +export { CaveatType } from './caveatBuilder'; + export { BalanceChangeType } from './caveatBuilder/types'; export { aggregateSignature } from './signatures'; diff --git a/packages/smart-accounts-kit/src/utils.ts b/packages/smart-accounts-kit/src/utils.ts index 5f6603cd..3680501a 100644 --- a/packages/smart-accounts-kit/src/utils.ts +++ b/packages/smart-accounts-kit/src/utils.ts @@ -1,6 +1,6 @@ import { type Hex, isHex, toHex } from 'viem'; -import { CaveatType } from './constants'; +import { CaveatType } from './caveatBuilder/coreCaveatBuilder'; /** * Validates that a caveat type is a recognized CaveatType enum value. @@ -8,17 +8,17 @@ import { CaveatType } from './constants'; * Custom caveat types registered via CaveatBuilder.extend() bypass this validation. * * @param type - The caveat type to validate (either enum value or string). - * @returns The validated caveat type. * @throws {Error} If the caveat type is not a recognized CaveatType enum value. */ -export function validateCaveatType(type: string | CaveatType): CaveatType { +export function validateCaveatType( + type: string | CaveatType, +): asserts type is CaveatType { const validTypes = Object.values(CaveatType); if (!validTypes.includes(type as CaveatType)) { throw new Error( `Invalid caveat type: "${type}". Valid types are: ${validTypes.join(', ')}`, ); } - return type as CaveatType; } /** diff --git a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts new file mode 100644 index 00000000..b5882690 --- /dev/null +++ b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts @@ -0,0 +1,446 @@ +import { concat } from 'viem'; +import { describe, it, expect } from 'vitest'; + +import { createCaveatBuilder, CaveatType } from '../../src/caveatBuilder'; +import { resolveCaveats } from '../../src/caveatBuilder/resolveCaveats'; +import { ScopeType } from '../../src/constants'; +import { createDelegation } from '../../src/delegation'; +import type { SmartAccountsEnvironment } from '../../src/types'; +import { randomAddress, randomBytes } from '../utils'; + +/** + * This test file demonstrates the CaveatType enum usage patterns. + * These tests verify that both enum references and string literals work interchangeably. + */ +describe('CaveatType enum usage patterns', () => { + const environment: SmartAccountsEnvironment = { + caveatEnforcers: { + AllowedMethodsEnforcer: randomBytes(20), + AllowedTargetsEnforcer: randomBytes(20), + ValueLteEnforcer: randomBytes(20), + ERC20TransferAmountEnforcer: randomBytes(20), + BlockNumberEnforcer: randomBytes(20), + LimitedCallsEnforcer: randomBytes(20), + IdEnforcer: randomBytes(20), + NonceEnforcer: randomBytes(20), + TimestampEnforcer: randomBytes(20), + NativeTokenTransferAmountEnforcer: randomBytes(20), + NativeBalanceChangeEnforcer: randomBytes(20), + NativeTokenPaymentEnforcer: randomBytes(20), + ERC20BalanceChangeEnforcer: randomBytes(20), + ERC721TransferEnforcer: randomBytes(20), + DeployedEnforcer: randomBytes(20), + AllowedCalldataEnforcer: randomBytes(20), + RedeemerEnforcer: randomBytes(20), + ArgsEqualityCheckEnforcer: randomBytes(20), + ERC20StreamingEnforcer: randomBytes(20), + NativeTokenStreamingEnforcer: randomBytes(20), + ERC721BalanceChangeEnforcer: randomBytes(20), + ERC1155BalanceChangeEnforcer: randomBytes(20), + SpecificActionERC20TransferBatchEnforcer: randomBytes(20), + ERC20PeriodTransferEnforcer: randomBytes(20), + NativeTokenPeriodTransferEnforcer: randomBytes(20), + ExactCalldataBatchEnforcer: randomBytes(20), + ExactCalldataEnforcer: randomBytes(20), + ExactExecutionEnforcer: randomBytes(20), + ExactExecutionBatchEnforcer: randomBytes(20), + MultiTokenPeriodEnforcer: randomBytes(20), + OwnershipTransferEnforcer: randomBytes(20), + }, + } as unknown as SmartAccountsEnvironment; + + describe('using CaveatType enum in CaveatBuilder.addCaveat()', () => { + it('should add caveat using CaveatType.AllowedMethods enum', () => { + const builder = createCaveatBuilder(environment); + const selectors = [randomBytes(4), randomBytes(4)]; + + // Using enum reference + const caveats = builder + .addCaveat(CaveatType.AllowedMethods, { selectors }) + .build(); + + expect(caveats).to.have.lengthOf(1); + expect(caveats[0]?.enforcer).to.equal( + environment.caveatEnforcers.AllowedMethodsEnforcer, + ); + expect(caveats[0]?.terms).to.equal(concat(selectors)); + }); + + it('should add caveat using CaveatType.AllowedTargets enum', () => { + const builder = createCaveatBuilder(environment); + const targets = [randomAddress(), randomAddress()]; + + // Using enum reference + const caveats = builder + .addCaveat(CaveatType.AllowedTargets, { targets }) + .build(); + + expect(caveats).to.have.lengthOf(1); + expect(caveats[0]?.enforcer).to.equal( + environment.caveatEnforcers.AllowedTargetsEnforcer, + ); + }); + + it('should add caveat using CaveatType.ValueLte enum', () => { + const builder = createCaveatBuilder(environment); + const maxValue = 1000n; + + // Using enum reference + const caveats = builder + .addCaveat(CaveatType.ValueLte, { maxValue }) + .build(); + + expect(caveats).to.have.lengthOf(1); + expect(caveats[0]?.enforcer).to.equal( + environment.caveatEnforcers.ValueLteEnforcer, + ); + }); + + it('should add caveat using CaveatType.LimitedCalls enum', () => { + const builder = createCaveatBuilder(environment); + const limit = 10; + + // Using enum reference + const caveats = builder + .addCaveat(CaveatType.LimitedCalls, { limit }) + .build(); + + expect(caveats).to.have.lengthOf(1); + expect(caveats[0]?.enforcer).to.equal( + environment.caveatEnforcers.LimitedCallsEnforcer, + ); + }); + + it('should add caveat using CaveatType.Erc20TransferAmount enum', () => { + const builder = createCaveatBuilder(environment); + const tokenAddress = randomAddress(); + const maxAmount = 1000n; + + // Using enum reference + const caveats = builder + .addCaveat(CaveatType.Erc20TransferAmount, { + tokenAddress, + maxAmount, + }) + .build(); + + expect(caveats).to.have.lengthOf(1); + expect(caveats[0]?.enforcer).to.equal( + environment.caveatEnforcers.ERC20TransferAmountEnforcer, + ); + }); + + it('should work identically with string literal (existing pattern)', () => { + const builderWithEnum = createCaveatBuilder(environment); + const builderWithString = createCaveatBuilder(environment); + const selectors = [randomBytes(4)]; + + const enumResult = builderWithEnum + .addCaveat(CaveatType.AllowedMethods, { selectors }) + .build(); + + const stringResult = builderWithString + .addCaveat('allowedMethods', { selectors }) + .build(); + + // Both should produce the same result + expect(enumResult).to.deep.equal(stringResult); + }); + }); + + describe('using CaveatType enum in createDelegation caveats array', () => { + const mockDelegator = randomAddress(); + const mockDelegate = randomAddress(); + + const smartAccountEnvironment: SmartAccountsEnvironment = { + caveatEnforcers: { + ValueLteEnforcer: randomAddress(), + ERC20TransferAmountEnforcer: randomAddress(), + AllowedMethodsEnforcer: randomAddress(), + BlockNumberEnforcer: randomAddress(), + AllowedTargetsEnforcer: randomAddress(), + LimitedCallsEnforcer: randomAddress(), + }, + } as unknown as SmartAccountsEnvironment; + + it('should create delegation with CaveatType enum in caveats array', () => { + const scope = { + type: ScopeType.Erc20TransferAmount as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + const result = createDelegation({ + environment: smartAccountEnvironment, + scope, + to: mockDelegate, + from: mockDelegator, + caveats: [ + { + type: CaveatType.LimitedCalls, + limit: 5, + }, + ], + }); + + // Should have scope caveats (valueLte + erc20TransferAmount) + 1 additional caveat + expect(result.caveats.length).to.be.greaterThan(2); + }); + + it('should create delegation with string literal in caveats array', () => { + const scope = { + type: 'erc20TransferAmount' as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + const result = createDelegation({ + environment: smartAccountEnvironment, + scope, + to: mockDelegate, + from: mockDelegator, + caveats: [ + { + type: 'limitedCalls', + limit: 5, + }, + ], + }); + + expect(result.caveats.length).to.be.greaterThan(2); + }); + + it('should mix CaveatType enum and string literals in caveats array', () => { + const scope = { + type: ScopeType.Erc20TransferAmount as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + const result = createDelegation({ + environment: smartAccountEnvironment, + scope, + to: mockDelegate, + from: mockDelegator, + caveats: [ + { type: CaveatType.LimitedCalls, limit: 5 }, + { type: 'allowedMethods', selectors: ['0x12345678'] }, + ], + }); + + // Should have scope caveats + 2 additional caveats + expect(result.caveats.length).to.be.greaterThan(3); + }); + + it('should create delegation with CaveatType.ValueLte enum', () => { + const scope = { + type: ScopeType.FunctionCall as const, + targets: [randomAddress()], + selectors: ['0x12345678'], + }; + + const result = createDelegation({ + environment: smartAccountEnvironment, + scope, + to: mockDelegate, + from: mockDelegator, + caveats: [ + { + type: CaveatType.ValueLte, + maxValue: 1000n, + }, + ], + }); + + expect(result.caveats.length).to.be.greaterThan(0); + }); + }); + + describe('using CaveatType enum in resolveCaveats', () => { + const envWithCaveats: SmartAccountsEnvironment = { + caveatEnforcers: { + ValueLteEnforcer: randomAddress(), + ERC20TransferAmountEnforcer: randomAddress(), + AllowedMethodsEnforcer: randomAddress(), + BlockNumberEnforcer: randomAddress(), + AllowedTargetsEnforcer: randomAddress(), + }, + } as unknown as SmartAccountsEnvironment; + + it('should resolve caveats with CaveatType enum', () => { + const scope = { + type: ScopeType.Erc20TransferAmount as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + const result = resolveCaveats({ + environment: envWithCaveats, + scope, + caveats: [ + { + type: CaveatType.AllowedMethods, + selectors: ['0xaabbccdd'], + }, + ], + }); + + expect(result).to.be.an('array'); + expect(result.length).to.be.greaterThan(2); // scope caveats + additional + }); + + it('should resolve caveats with string literal', () => { + const scope = { + type: 'erc20TransferAmount' as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + const result = resolveCaveats({ + environment: envWithCaveats, + scope, + caveats: [ + { + type: 'allowedMethods', + selectors: ['0xaabbccdd'], + }, + ], + }); + + expect(result).to.be.an('array'); + expect(result.length).to.be.greaterThan(2); + }); + + it('should resolve caveats with mixed enum and string', () => { + const scope = { + type: ScopeType.Erc20TransferAmount as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + const result = resolveCaveats({ + environment: envWithCaveats, + scope, + caveats: [ + { type: CaveatType.AllowedMethods, selectors: ['0xaabbccdd'] }, + { type: 'allowedTargets', targets: [randomAddress()] }, + { + type: CaveatType.BlockNumber, + afterThreshold: 0n, + beforeThreshold: 1000n, + }, + ], + }); + + expect(result).to.be.an('array'); + expect(result.length).to.be.greaterThan(4); + }); + }); + + describe('using ScopeType enum in scope config', () => { + it('should create delegation with ScopeType enum reference', () => { + const smartAccountEnvironment: SmartAccountsEnvironment = { + caveatEnforcers: { + ValueLteEnforcer: randomAddress(), + ERC20TransferAmountEnforcer: randomAddress(), + }, + } as unknown as SmartAccountsEnvironment; + + // Using enum reference for scope type + const result = createDelegation({ + environment: smartAccountEnvironment, + scope: { + type: ScopeType.Erc20TransferAmount as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }, + to: randomAddress(), + from: randomAddress(), + }); + + expect(result.caveats.length).to.be.greaterThan(0); + }); + + it('should create delegation with string literal scope type', () => { + const smartAccountEnvironment: SmartAccountsEnvironment = { + caveatEnforcers: { + ValueLteEnforcer: randomAddress(), + ERC20TransferAmountEnforcer: randomAddress(), + }, + } as unknown as SmartAccountsEnvironment; + + // Using string literal for scope type + const result = createDelegation({ + environment: smartAccountEnvironment, + scope: { + type: 'erc20TransferAmount' as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }, + to: randomAddress(), + from: randomAddress(), + }); + + expect(result.caveats.length).to.be.greaterThan(0); + }); + }); + + describe('TypeScript type inference verification', () => { + // These tests verify TypeScript types are correctly inferred + // If TypeScript compilation passes, the types are correct + + it('should allow CaveatType enum in addCaveat', () => { + const builder = createCaveatBuilder(environment); + + // This should compile without errors if types are correct + builder.addCaveat(CaveatType.AllowedMethods, { + selectors: ['0x12345678'], + }); + builder.addCaveat(CaveatType.ValueLte, { maxValue: 1000n }); + builder.addCaveat(CaveatType.AllowedTargets, { + targets: [randomAddress()], + }); + + const caveats = builder.build(); + expect(caveats).to.have.lengthOf(3); + }); + + it('should allow string literals in addCaveat', () => { + const builder = createCaveatBuilder(environment); + + // This should also compile without errors + builder.addCaveat('allowedMethods', { + selectors: ['0x12345678'], + }); + builder.addCaveat('valueLte', { maxValue: 1000n }); + builder.addCaveat('allowedTargets', { + targets: [randomAddress()], + }); + + const caveats = builder.build(); + expect(caveats).to.have.lengthOf(3); + }); + + it('should allow CaveatType enum in caveats array', () => { + const scope = { + type: ScopeType.Erc20TransferAmount as const, + tokenAddress: randomAddress(), + maxAmount: 100n, + }; + + // This should compile if types are correct + const result = createDelegation({ + environment, + scope, + to: randomAddress(), + from: randomAddress(), + caveats: [ + { type: CaveatType.LimitedCalls, limit: 10 }, + { type: CaveatType.ValueLte, maxValue: 500n }, + ], + }); + + expect(result.caveats.length).to.be.greaterThan(2); + }); + }); +}); From 3c3953d27e678805b9d662e9efe92edff25ec3ec Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 26 Feb 2026 21:08:30 +1300 Subject: [PATCH 06/18] refactor: update CoreCaveatMap to use CaveatType enum for improved type safety --- .../src/caveatBuilder/coreCaveatBuilder.ts | 63 ++++++++++--------- 1 file changed, 32 insertions(+), 31 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 398aa9dc..f11b1e42 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -128,37 +128,38 @@ export enum CaveatType { // declaring the return type of createCaveatBuilder, we ensure the caveat // map remains synchronized with the actual implementation. type CoreCaveatMap = { - allowedMethods: typeof allowedMethodsBuilder; - allowedTargets: typeof allowedTargetsBuilder; - deployed: typeof deployedBuilder; - allowedCalldata: typeof allowedCalldataBuilder; - erc20BalanceChange: typeof erc20BalanceChangeBuilder; - erc721BalanceChange: typeof erc721BalanceChangeBuilder; - erc1155BalanceChange: typeof erc1155BalanceChangeBuilder; - valueLte: typeof valueLteBuilder; - limitedCalls: typeof limitedCallsBuilder; - id: typeof idBuilder; - nonce: typeof nonceBuilder; - timestamp: typeof timestampBuilder; - blockNumber: typeof blockNumberBuilder; - erc20TransferAmount: typeof erc20TransferAmountBuilder; - erc20Streaming: typeof erc20StreamingBuilder; - nativeTokenStreaming: typeof nativeTokenStreamingBuilder; - erc721Transfer: typeof erc721TransferBuilder; - nativeTokenTransferAmount: typeof nativeTokenTransferAmountBuilder; - nativeBalanceChange: typeof nativeBalanceChangeBuilder; - redeemer: typeof redeemerBuilder; - nativeTokenPayment: typeof nativeTokenPaymentBuilder; - argsEqualityCheck: typeof argsEqualityCheckBuilder; - specificActionERC20TransferBatch: typeof specificActionERC20TransferBatchBuilder; - erc20PeriodTransfer: typeof erc20PeriodTransferBuilder; - nativeTokenPeriodTransfer: typeof nativeTokenPeriodTransferBuilder; - exactCalldataBatch: typeof exactCalldataBatchBuilder; - exactCalldata: typeof exactCalldataBuilder; - exactExecution: typeof exactExecutionBuilder; - exactExecutionBatch: typeof exactExecutionBatchBuilder; - multiTokenPeriod: typeof multiTokenPeriodBuilder; - ownershipTransfer: typeof ownershipTransferBuilder; + [CaveatType.AllowedMethods]: typeof allowedMethodsBuilder; + [CaveatType.AllowedTargets]: typeof allowedTargetsBuilder; + [CaveatType.Deployed]: typeof deployedBuilder; + [CaveatType.AllowedCalldata]: typeof allowedCalldataBuilder; + [CaveatType.Erc20BalanceChange]: typeof erc20BalanceChangeBuilder; + [CaveatType.Erc721BalanceChange]: typeof erc721BalanceChangeBuilder; + [CaveatType.Erc1155BalanceChange]: typeof erc1155BalanceChangeBuilder; + [CaveatType.ValueLte]: typeof valueLteBuilder; + [CaveatType.LimitedCalls]: typeof limitedCallsBuilder; + [CaveatType.Id]: typeof idBuilder; + [CaveatType.Nonce]: typeof nonceBuilder; + [CaveatType.Timestamp]: typeof timestampBuilder; + [CaveatType.BlockNumber]: typeof blockNumberBuilder; + [CaveatType.Erc20TransferAmount]: typeof erc20TransferAmountBuilder; + [CaveatType.Erc20Streaming]: typeof erc20StreamingBuilder; + [CaveatType.NativeTokenStreaming]: typeof nativeTokenStreamingBuilder; + [CaveatType.Erc721Transfer]: typeof erc721TransferBuilder; + [CaveatType.NativeTokenStreaming]: typeof nativeTokenStreamingBuilder; + [CaveatType.NativeTokenTransferAmount]: typeof nativeTokenTransferAmountBuilder; + [CaveatType.NativeBalanceChange]: typeof nativeBalanceChangeBuilder; + [CaveatType.Redeemer]: typeof redeemerBuilder; + [CaveatType.NativeTokenPayment]: typeof nativeTokenPaymentBuilder; + [CaveatType.ArgsEqualityCheck]: typeof argsEqualityCheckBuilder; + [CaveatType.SpecificActionERC20TransferBatch]: typeof specificActionERC20TransferBatchBuilder; + [CaveatType.Erc20PeriodTransfer]: typeof erc20PeriodTransferBuilder; + [CaveatType.NativeTokenPeriodTransfer]: typeof nativeTokenPeriodTransferBuilder; + [CaveatType.ExactCalldataBatch]: typeof exactCalldataBatchBuilder; + [CaveatType.ExactCalldata]: typeof exactCalldataBuilder; + [CaveatType.ExactExecution]: typeof exactExecutionBuilder; + [CaveatType.ExactExecutionBatch]: typeof exactExecutionBatchBuilder; + [CaveatType.MultiTokenPeriod]: typeof multiTokenPeriodBuilder; + [CaveatType.OwnershipTransfer]: typeof ownershipTransferBuilder; }; /** From 755b3e05689bc3631e48e1eb8ac52096fb83c039 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Thu, 26 Feb 2026 21:20:27 +1300 Subject: [PATCH 07/18] refactor: remove duplicate entry for NativeTokenStreaming in CoreCaveatMap --- .../smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index f11b1e42..0a5f377f 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -145,7 +145,6 @@ type CoreCaveatMap = { [CaveatType.Erc20Streaming]: typeof erc20StreamingBuilder; [CaveatType.NativeTokenStreaming]: typeof nativeTokenStreamingBuilder; [CaveatType.Erc721Transfer]: typeof erc721TransferBuilder; - [CaveatType.NativeTokenStreaming]: typeof nativeTokenStreamingBuilder; [CaveatType.NativeTokenTransferAmount]: typeof nativeTokenTransferAmountBuilder; [CaveatType.NativeBalanceChange]: typeof nativeBalanceChangeBuilder; [CaveatType.Redeemer]: typeof redeemerBuilder; From 8cfe0a73780c5da578c2c6e3be596cf0370a2d88 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Fri, 27 Feb 2026 14:19:24 +1300 Subject: [PATCH 08/18] refactor: replace string literals with CaveatType enums in caveat builders - Updated caveat builders to use the CaveatType enum instead of string literals for better type safety and maintainability. - Removed the validateCaveatType function from utils as it is no longer necessary. - Adjusted tests to reflect the changes in caveat type usage. --- .../test/caveats/allowedCalldata.test.ts | 5 +- .../test/caveats/allowedMethods.test.ts | 6 +-- .../test/caveats/allowedTargets.test.ts | 5 +- .../test/caveats/argsEqualityCheck.test.ts | 5 +- .../test/caveats/blockNumber.test.ts | 5 +- .../test/caveats/caveatUtils.test.ts | 8 ++-- .../test/caveats/deployed.test.ts | 5 +- .../test/caveats/erc20PeriodTransfer.test.ts | 20 ++++---- .../test/caveats/erc20Streaming.test.ts | 11 +++-- .../test/caveats/erc20TransferAmount.test.ts | 12 ++--- .../test/caveats/erc721Transfer.test.ts | 5 +- .../test/caveats/exactCalldata.test.ts | 4 +- .../test/caveats/exactCalldataBatch.test.ts | 5 +- .../test/caveats/exactExecution.test.ts | 5 +- .../test/caveats/exactExecutionBatch.test.ts | 5 +- .../delegator-e2e/test/caveats/id.test.ts | 5 +- .../caveats/idEnforcerContractRead.test.ts | 11 +++-- .../test/caveats/limitedCalls.test.ts | 13 ++--- .../test/caveats/multiTokenPeriod.test.ts | 4 +- .../test/caveats/nativeBalanceChange.test.ts | 7 +-- .../test/caveats/nativeTokenPayment.test.ts | 14 +++--- .../caveats/nativeTokenPeriodTransfer.test.ts | 30 ++++++------ .../test/caveats/nativeTokenStreaming.test.ts | 26 +++++----- .../caveats/nativeTokenTransferAmount.test.ts | 24 +++++----- .../delegator-e2e/test/caveats/nonce.test.ts | 5 +- .../test/caveats/redeemer.test.ts | 5 +- .../specificActionERC20TransferBatch.test.ts | 12 ++--- .../test/caveats/timestamp.test.ts | 5 +- .../test/caveats/valueLte.test.ts | 3 +- .../caveatBuilder/allowedCalldataBuilder.ts | 3 +- .../caveatBuilder/allowedMethodsBuilder.ts | 3 +- .../caveatBuilder/allowedTargetsBuilder.ts | 3 +- .../caveatBuilder/argsEqualityCheckBuilder.ts | 3 +- .../src/caveatBuilder/blockNumberBuilder.ts | 3 +- .../src/caveatBuilder/caveatType.ts | 38 +++++++++++++++ .../src/caveatBuilder/coreCaveatBuilder.ts | 43 ++--------------- .../src/caveatBuilder/deployedBuilder.ts | 3 +- .../erc1155BalanceChangeBuilder.ts | 3 +- .../erc20BalanceChangeBuilder.ts | 3 +- .../erc20PeriodTransferBuilder.ts | 3 +- .../caveatBuilder/erc20StreamingBuilder.ts | 3 +- .../erc20TransferAmountBuilder.ts | 3 +- .../erc721BalanceChangeBuilder.ts | 3 +- .../caveatBuilder/erc721TransferBuilder.ts | 3 +- .../exactCalldataBatchBuilder.ts | 3 +- .../src/caveatBuilder/exactCalldataBuilder.ts | 3 +- .../exactExecutionBatchBuilder.ts | 3 +- .../caveatBuilder/exactExecutionBuilder.ts | 3 +- .../src/caveatBuilder/idBuilder.ts | 3 +- .../src/caveatBuilder/limitedCallsBuilder.ts | 3 +- .../caveatBuilder/multiTokenPeriodBuilder.ts | 3 +- .../nativeBalanceChangeBuilder.ts | 3 +- .../nativeTokenPaymentBuilder.ts | 3 +- .../nativeTokenPeriodTransferBuilder.ts | 3 +- .../nativeTokenStreamingBuilder.ts | 3 +- .../nativeTokenTransferAmountBuilder.ts | 3 +- .../src/caveatBuilder/nonceBuilder.ts | 3 +- .../caveatBuilder/ownershipTransferBuilder.ts | 3 +- .../src/caveatBuilder/redeemerBuilder.ts | 3 +- .../src/caveatBuilder/resolveCaveats.ts | 5 +- .../caveatBuilder/scope/erc20PeriodicScope.ts | 6 +-- .../scope/erc20StreamingScope.ts | 6 +-- .../caveatBuilder/scope/erc20TransferScope.ts | 6 +-- .../src/caveatBuilder/scope/erc721Scope.ts | 4 +- .../caveatBuilder/scope/functionCallScope.ts | 12 ++--- .../scope/nativeTokenPeriodicScope.ts | 10 ++-- .../scope/nativeTokenStreamingScope.ts | 10 ++-- .../scope/nativeTokenTransferScope.ts | 10 ++-- .../src/caveatBuilder/scope/ownershipScope.ts | 4 +- ...specificActionERC20TransferBatchBuilder.ts | 3 +- .../src/caveatBuilder/timestampBuilder.ts | 3 +- .../src/caveatBuilder/valueLteBuilder.ts | 3 +- packages/smart-accounts-kit/src/utils.ts | 21 -------- .../test/caveatBuilder/caveatTypeEnum.test.ts | 8 ++-- .../caveatBuilder/createCaveatBuilder.test.ts | 48 ++++++++++++------- .../test/caveatBuilder/resolveCaveats.test.ts | 9 ++-- 76 files changed, 324 insertions(+), 279 deletions(-) create mode 100644 packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts diff --git a/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts b/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts index b7315bb0..9cba467f 100644 --- a/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts +++ b/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -140,7 +141,7 @@ const runTest_expectSuccess = async ( salt: '0x0', caveats: caveats .reduce((builder, caveat) => { - builder.addCaveat('allowedCalldata', { + builder.addCaveat(CaveatType.AllowedCalldata, { startIndex: caveat.from, value: caveat.calldata, }); @@ -218,7 +219,7 @@ const runTest_expectFailure = async ( salt: '0x0', caveats: caveats .reduce((builder, caveat) => { - builder.addCaveat('allowedCalldata', { + builder.addCaveat(CaveatType.AllowedCalldata, { startIndex: caveat.from, value: caveat.calldata, }); diff --git a/packages/delegator-e2e/test/caveats/allowedMethods.test.ts b/packages/delegator-e2e/test/caveats/allowedMethods.test.ts index 42ea4cb6..25b69db1 100644 --- a/packages/delegator-e2e/test/caveats/allowedMethods.test.ts +++ b/packages/delegator-e2e/test/caveats/allowedMethods.test.ts @@ -15,7 +15,7 @@ import type { MetaMaskSmartAccount, Delegation, } from '@metamask/smart-accounts-kit'; -import { createCaveatBuilder } from '@metamask/smart-accounts-kit/utils'; +import { createCaveatBuilder, CaveatType } from '@metamask/smart-accounts-kit/utils'; import { gasPrice, sponsoredBundlerClient, @@ -146,7 +146,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('allowedMethods', { selectors: allowedMethods }) + .addCaveat(CaveatType.AllowedMethods, { selectors: allowedMethods }) .build(), signature: '0x', }; @@ -216,7 +216,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('allowedMethods', { selectors: allowedMethods }) + .addCaveat(CaveatType.AllowedMethods, { selectors: allowedMethods }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/allowedTargets.test.ts b/packages/delegator-e2e/test/caveats/allowedTargets.test.ts index fcff935f..d91cacc6 100644 --- a/packages/delegator-e2e/test/caveats/allowedTargets.test.ts +++ b/packages/delegator-e2e/test/caveats/allowedTargets.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -114,7 +115,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('allowedTargets', { targets: allowedTargets }) + .addCaveat(CaveatType.AllowedTargets, { targets: allowedTargets }) .build(), signature: '0x', }; @@ -184,7 +185,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('allowedTargets', { targets: allowedTargets }) + .addCaveat(CaveatType.AllowedTargets, { targets: allowedTargets }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/argsEqualityCheck.test.ts b/packages/delegator-e2e/test/caveats/argsEqualityCheck.test.ts index 9a90e76d..bb1153b2 100644 --- a/packages/delegator-e2e/test/caveats/argsEqualityCheck.test.ts +++ b/packages/delegator-e2e/test/caveats/argsEqualityCheck.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -138,7 +139,7 @@ const runTest_expectSuccess = async (args: Hex, actualArgs: Hex) => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('argsEqualityCheck', { args }) + .addCaveat(CaveatType.ArgsEqualityCheck, { args }) .build(), signature: '0x', }; @@ -210,7 +211,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('argsEqualityCheck', { args }) + .addCaveat(CaveatType.ArgsEqualityCheck, { args }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/blockNumber.test.ts b/packages/delegator-e2e/test/caveats/blockNumber.test.ts index a1870ee8..5a7c9dc2 100644 --- a/packages/delegator-e2e/test/caveats/blockNumber.test.ts +++ b/packages/delegator-e2e/test/caveats/blockNumber.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -181,7 +182,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('blockNumber', { + .addCaveat(CaveatType.BlockNumber, { afterThreshold, beforeThreshold, }) @@ -257,7 +258,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('blockNumber', { + .addCaveat(CaveatType.BlockNumber, { afterThreshold, beforeThreshold, }) diff --git a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts index 578b1c72..f9182e49 100644 --- a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts +++ b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts @@ -623,7 +623,7 @@ describe('MultiTokenPeriodEnforcer', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', { + .addCaveat(CaveatType.MultiTokenPeriod, { tokenConfigs: [ { token: erc20TokenAddress, @@ -721,7 +721,7 @@ describe('MultiTokenPeriodEnforcer', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', { + .addCaveat(CaveatType.MultiTokenPeriod, { tokenConfigs: [ { token: erc20TokenAddress, @@ -809,7 +809,7 @@ describe('MultiTokenPeriodEnforcer', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', { + .addCaveat(CaveatType.MultiTokenPeriod, { tokenConfigs: [ { token: erc20TokenAddress, @@ -1507,7 +1507,7 @@ describe('Individual action functions vs client extension methods', () => { delegator: aliceSmartAccount.address, authority: ROOT_AUTHORITY as Address, caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('multiTokenPeriod', { + .addCaveat(CaveatType.MultiTokenPeriod, { tokenConfigs: [ { token: erc20TokenAddress, diff --git a/packages/delegator-e2e/test/caveats/deployed.test.ts b/packages/delegator-e2e/test/caveats/deployed.test.ts index e2b62b96..ffe2106a 100644 --- a/packages/delegator-e2e/test/caveats/deployed.test.ts +++ b/packages/delegator-e2e/test/caveats/deployed.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -160,7 +161,7 @@ const runTest_expectSuccess = async (deployedAddress: Hex, salt: Hex) => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('deployed', { + .addCaveat(CaveatType.Deployed, { contractAddress: deployedAddress, salt, bytecode: CounterMetadata.bytecode.object as Hex, @@ -247,7 +248,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('deployed', { + .addCaveat(CaveatType.Deployed, { contractAddress: deployedAddress, salt, bytecode: CounterMetadata.bytecode.object as Hex, diff --git a/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts b/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts index 57ecaf9f..23000a50 100644 --- a/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts +++ b/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts @@ -111,7 +111,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -196,7 +196,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -342,7 +342,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -414,7 +414,7 @@ test('Bob attempts to redeem with invalid execution length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -488,7 +488,7 @@ test('Bob attempts to redeem with invalid contract', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -558,7 +558,7 @@ test('Bob attempts to redeem with invalid method', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -628,7 +628,7 @@ test('Bob attempts to redeem with zero start date', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, @@ -705,7 +705,7 @@ test('Bob attempts to redeem with zero period amount', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount: 1n, // we need a valid period amount to pass validation periodDuration, @@ -783,7 +783,7 @@ test('Bob attempts to redeem with zero period duration', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration: 1, // we need a valid period duration to pass validation @@ -861,7 +861,7 @@ test('Bob attempts to redeem before start date', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: erc20TokenAddress, periodAmount, periodDuration, diff --git a/packages/delegator-e2e/test/caveats/erc20Streaming.test.ts b/packages/delegator-e2e/test/caveats/erc20Streaming.test.ts index 2a64ea2c..dba6aad7 100644 --- a/packages/delegator-e2e/test/caveats/erc20Streaming.test.ts +++ b/packages/delegator-e2e/test/caveats/erc20Streaming.test.ts @@ -14,6 +14,7 @@ import type { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -393,7 +394,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20Streaming', { + .addCaveat(CaveatType.Erc20Streaming, { tokenAddress: erc20TokenAddress, initialAmount, maxAmount, @@ -469,7 +470,7 @@ test('Bob attempts to redeem with maxAmount less than initialAmount', async () = const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20Streaming', { + .addCaveat(CaveatType.Erc20Streaming, { tokenAddress: erc20TokenAddress, initialAmount, maxAmount: initialAmount + 1n, // we need a valid maxAmount @@ -551,7 +552,7 @@ test('Bob attempts to redeem with zero start time', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('erc20Streaming', { + .addCaveat(CaveatType.Erc20Streaming, { tokenAddress: erc20TokenAddress, initialAmount, maxAmount, @@ -640,7 +641,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc20Streaming', { + .addCaveat(CaveatType.Erc20Streaming, { tokenAddress: erc20TokenAddress, initialAmount, maxAmount, @@ -731,7 +732,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc20Streaming', { + .addCaveat(CaveatType.Erc20Streaming, { tokenAddress: erc20TokenAddress, initialAmount, maxAmount, diff --git a/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts b/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts index 145af698..9314cf78 100644 --- a/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts +++ b/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts @@ -99,7 +99,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: erc20TokenAddress, maxAmount, }) @@ -194,7 +194,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: erc20TokenAddress, maxAmount, }) @@ -286,7 +286,7 @@ describe('ERC20TransferAmountEnforcer Utilities E2E Tests', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: erc20TokenAddress, maxAmount, }) @@ -341,7 +341,7 @@ describe('ERC20TransferAmountEnforcer Utilities E2E Tests', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: erc20TokenAddress, maxAmount, }) @@ -510,7 +510,7 @@ describe('ERC20TransferAmountEnforcer Utilities E2E Tests', () => { authority: ROOT_AUTHORITY, salt: '0x1', // Different salt to avoid conflicts caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: erc20TokenAddress, maxAmount, }) @@ -635,7 +635,7 @@ describe('ERC20TransferAmountEnforcer Utilities E2E Tests', () => { authority: ROOT_AUTHORITY, salt: '0x2', // Different salt caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: erc20TokenAddress, maxAmount, }) diff --git a/packages/delegator-e2e/test/caveats/erc721Transfer.test.ts b/packages/delegator-e2e/test/caveats/erc721Transfer.test.ts index b1f1de4b..bf15b96b 100644 --- a/packages/delegator-e2e/test/caveats/erc721Transfer.test.ts +++ b/packages/delegator-e2e/test/caveats/erc721Transfer.test.ts @@ -13,6 +13,7 @@ import type { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -95,7 +96,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc721Transfer', { + .addCaveat(CaveatType.Erc721Transfer, { tokenAddress, tokenId, }) @@ -195,7 +196,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('erc721Transfer', { + .addCaveat(CaveatType.Erc721Transfer, { tokenAddress, tokenId: allowedTokenId, }) diff --git a/packages/delegator-e2e/test/caveats/exactCalldata.test.ts b/packages/delegator-e2e/test/caveats/exactCalldata.test.ts index 1e29ba9d..6deeb5a7 100644 --- a/packages/delegator-e2e/test/caveats/exactCalldata.test.ts +++ b/packages/delegator-e2e/test/caveats/exactCalldata.test.ts @@ -81,7 +81,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactCalldata', { calldata }) + .addCaveat(CaveatType.ExactCalldata, { calldata }) .build(), signature: '0x', }; @@ -141,7 +141,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactCalldata', { calldata: expectedCalldata }) + .addCaveat(CaveatType.ExactCalldata, { calldata: expectedCalldata }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/exactCalldataBatch.test.ts b/packages/delegator-e2e/test/caveats/exactCalldataBatch.test.ts index ceb55496..37ceaa5d 100644 --- a/packages/delegator-e2e/test/caveats/exactCalldataBatch.test.ts +++ b/packages/delegator-e2e/test/caveats/exactCalldataBatch.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -84,7 +85,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactCalldataBatch', { executions: expectedExecutions }) + .addCaveat(CaveatType.ExactCalldataBatch, { executions: expectedExecutions }) .build(), signature: '0x', }; @@ -139,7 +140,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactCalldataBatch', { executions: expectedExecutions }) + .addCaveat(CaveatType.ExactCalldataBatch, { executions: expectedExecutions }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/exactExecution.test.ts b/packages/delegator-e2e/test/caveats/exactExecution.test.ts index e6ff3b40..ec1f890c 100644 --- a/packages/delegator-e2e/test/caveats/exactExecution.test.ts +++ b/packages/delegator-e2e/test/caveats/exactExecution.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -80,7 +81,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactExecution', { execution }) + .addCaveat(CaveatType.ExactExecution, { execution }) .build(), signature: '0x', }; @@ -135,7 +136,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactExecution', { execution: expectedExecution }) + .addCaveat(CaveatType.ExactExecution, { execution: expectedExecution }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/exactExecutionBatch.test.ts b/packages/delegator-e2e/test/caveats/exactExecutionBatch.test.ts index a844e269..8e8185a0 100644 --- a/packages/delegator-e2e/test/caveats/exactExecutionBatch.test.ts +++ b/packages/delegator-e2e/test/caveats/exactExecutionBatch.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -83,7 +84,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactExecutionBatch', { executions }) + .addCaveat(CaveatType.ExactExecutionBatch, { executions }) .build(), signature: '0x', }; @@ -138,7 +139,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('exactExecutionBatch', { executions: expectedExecutions }) + .addCaveat(CaveatType.ExactExecutionBatch, { executions: expectedExecutions }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/id.test.ts b/packages/delegator-e2e/test/caveats/id.test.ts index 90842461..d5924342 100644 --- a/packages/delegator-e2e/test/caveats/id.test.ts +++ b/packages/delegator-e2e/test/caveats/id.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -114,7 +115,7 @@ const runTest_expectSuccess = async (idValue: number) => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('id', { id: idValue }) + .addCaveat(CaveatType.Id, { id: idValue }) .build(), signature: '0x', }; @@ -187,7 +188,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('id', { id: idValue }) + .addCaveat(CaveatType.Id, { id: idValue }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/idEnforcerContractRead.test.ts b/packages/delegator-e2e/test/caveats/idEnforcerContractRead.test.ts index 0798914e..0a9947b2 100644 --- a/packages/delegator-e2e/test/caveats/idEnforcerContractRead.test.ts +++ b/packages/delegator-e2e/test/caveats/idEnforcerContractRead.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -145,7 +146,7 @@ describe('IdEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('id', { id }) + .addCaveat(CaveatType.Id, { id }) .build(), signature: '0x', }; @@ -246,7 +247,7 @@ describe('IdEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('id', { id }) + .addCaveat(CaveatType.Id, { id }) .build(), signature: '0x', }; @@ -360,7 +361,7 @@ describe('IdEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('id', { id: id2 }) + .addCaveat(CaveatType.Id, { id: id2 }) .build(), signature: '0x', }; @@ -452,7 +453,7 @@ describe('IdEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('id', { id }) + .addCaveat(CaveatType.Id, { id }) .build(), signature: '0x', }; @@ -520,7 +521,7 @@ describe('IdEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x1', // Different salt caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('id', { id }) + .addCaveat(CaveatType.Id, { id }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/limitedCalls.test.ts b/packages/delegator-e2e/test/caveats/limitedCalls.test.ts index 56963f63..7596b836 100644 --- a/packages/delegator-e2e/test/caveats/limitedCalls.test.ts +++ b/packages/delegator-e2e/test/caveats/limitedCalls.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, hashDelegation, @@ -111,7 +112,7 @@ const runTest = async (limit: number, runs: number) => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('limitedCalls', { limit }) + .addCaveat(CaveatType.LimitedCalls, { limit }) .build(), signature: '0x', }; @@ -203,7 +204,7 @@ describe('LimitedCallsEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('limitedCalls', { limit }) + .addCaveat(CaveatType.LimitedCalls, { limit }) .build(), signature: '0x', }; @@ -231,7 +232,7 @@ describe('LimitedCallsEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('limitedCalls', { limit }) + .addCaveat(CaveatType.LimitedCalls, { limit }) .build(), signature: '0x', }; @@ -318,7 +319,7 @@ describe('LimitedCallsEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x1', caveats: createCaveatBuilder(environment) - .addCaveat('limitedCalls', { limit }) + .addCaveat(CaveatType.LimitedCalls, { limit }) .build(), signature: '0x', }; @@ -329,7 +330,7 @@ describe('LimitedCallsEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x2', caveats: createCaveatBuilder(environment) - .addCaveat('limitedCalls', { limit }) + .addCaveat(CaveatType.LimitedCalls, { limit }) .build(), signature: '0x', }; @@ -412,7 +413,7 @@ describe('LimitedCallsEnforcer Contract Read Methods', () => { authority: ROOT_AUTHORITY, salt: '0x42', caveats: createCaveatBuilder(environment) - .addCaveat('limitedCalls', { limit }) + .addCaveat(CaveatType.LimitedCalls, { limit }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts b/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts index 240c3b6b..8951ecc4 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', { tokenConfigs: configs }) + .addCaveat(CaveatType.MultiTokenPeriod, { tokenConfigs: configs }) .build(), signature: '0x', }; @@ -215,7 +215,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('multiTokenPeriod', { tokenConfigs: configs }) + .addCaveat(CaveatType.MultiTokenPeriod, { tokenConfigs: configs }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/nativeBalanceChange.test.ts b/packages/delegator-e2e/test/caveats/nativeBalanceChange.test.ts index 72f9509e..b3bfc19f 100644 --- a/packages/delegator-e2e/test/caveats/nativeBalanceChange.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeBalanceChange.test.ts @@ -11,6 +11,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -123,7 +124,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeBalanceChange', { + .addCaveat(CaveatType.NativeBalanceChange, { recipient, balance: requiredIncrease, changeType: BalanceChangeType.Increase, @@ -200,7 +201,7 @@ const testRun_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeBalanceChange', { + .addCaveat(CaveatType.NativeBalanceChange, { recipient: target, balance: requiredChange, changeType, @@ -290,7 +291,7 @@ const testRun_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeBalanceChange', { + .addCaveat(CaveatType.NativeBalanceChange, { recipient: target, balance: requiredChange, changeType, diff --git a/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts index 643d0ce1..4e3eb8aa 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts @@ -84,7 +84,7 @@ test('maincase: Bob redeems the delegation with a permissions context allowing p authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPayment', { + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount: requiredValue, }) @@ -103,7 +103,7 @@ test('maincase: Bob redeems the delegation with a permissions context allowing p authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('argsEqualityCheck', { + .addCaveat(CaveatType.ArgsEqualityCheck, { args, }) .build(), @@ -137,7 +137,7 @@ test('Bob attempts to redeem the delegation without an argsEqualityCheckEnforcer authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPayment', { + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount: requiredValue, }) @@ -184,7 +184,7 @@ test('Bob attempts to redeem the delegation without providing a valid permission authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPayment', { + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount: requiredValue, }) @@ -208,7 +208,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenPayment', { + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount: requiredValue, }) @@ -237,7 +237,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('argsEqualityCheck', { + .addCaveat(CaveatType.ArgsEqualityCheck, { args, }) .build(), @@ -272,7 +272,7 @@ test('Bob attempts to redeem with empty allowance delegations', async () => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nativeTokenPayment', { + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount: requiredValue, }) diff --git a/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts index 14742353..9f38d1be 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts @@ -96,7 +96,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, @@ -170,7 +170,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, @@ -301,7 +301,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, @@ -366,7 +366,7 @@ test('Bob attempts to redeem with zero start date', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate: currentTime, // valid start date @@ -435,7 +435,7 @@ test('Bob attempts to redeem with zero period amount', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount: 1n, // valid period amount periodDuration, startDate, @@ -504,7 +504,7 @@ test('Bob attempts to redeem with zero period duration', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration: 3600, // valid period duration startDate, @@ -573,7 +573,7 @@ test('Bob attempts to redeem before start date', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, @@ -799,12 +799,12 @@ test('Caveat with exactCalldata: Bob successfully redeems with exact calldata ma authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, }) - .addCaveat('exactCalldata', { calldata: exactCalldata }) + .addCaveat(CaveatType.ExactCalldata, { calldata: exactCalldata }) .build(), signature: '0x', }; @@ -891,12 +891,12 @@ test('Caveat with exactCalldata: Bob fails to redeem with wrong calldata', async authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, }) - .addCaveat('exactCalldata', { calldata: exactCalldata }) + .addCaveat(CaveatType.ExactCalldata, { calldata: exactCalldata }) .build(), signature: '0x', }; @@ -956,12 +956,12 @@ test('Caveat with allowedCalldata: Bob successfully redeems with allowed calldat authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, }) - .addCaveat('allowedCalldata', allowedCalldata) + .addCaveat(CaveatType.AllowedCalldata, allowedCalldata) .build(), signature: '0x', }; @@ -1047,12 +1047,12 @@ test('Caveat with allowedCalldata: Bob fails to redeem with disallowed calldata authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenPeriodTransfer', { + .addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, }) - .addCaveat('allowedCalldata', allowedCalldata) + .addCaveat(CaveatType.AllowedCalldata, allowedCalldata) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts index cb75d604..683f930f 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts @@ -325,7 +325,7 @@ test('Bob attempts to redeem with invalid terms length', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, @@ -394,7 +394,7 @@ test('Bob attempts to redeem with invalid max amount', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount: initialAmount + 1n, // we need a valid maxAmount amountPerSecond, @@ -467,7 +467,7 @@ test('Bob attempts to redeem with zero start time', async () => { const { environment } = aliceSmartAccount; const caveats = createCaveatBuilder(environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, @@ -548,7 +548,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, @@ -630,7 +630,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, @@ -892,13 +892,13 @@ test('Caveat with exactCalldata: Bob successfully redeems with exact calldata ma authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, startTime, }) - .addCaveat('exactCalldata', { calldata: exactCalldata }) + .addCaveat(CaveatType.ExactCalldata, { calldata: exactCalldata }) .build(), signature: '0x', }; @@ -986,13 +986,13 @@ test('Caveat with exactCalldata: Bob fails to redeem with wrong calldata', async authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, startTime, }) - .addCaveat('exactCalldata', { calldata: exactCalldata }) + .addCaveat(CaveatType.ExactCalldata, { calldata: exactCalldata }) .build(), signature: '0x', }; @@ -1053,13 +1053,13 @@ test('Caveat with allowedCalldata: Bob successfully redeems with allowed calldat authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, startTime, }) - .addCaveat('allowedCalldata', allowedCalldata) + .addCaveat(CaveatType.AllowedCalldata, allowedCalldata) .build(), signature: '0x', }; @@ -1146,13 +1146,13 @@ test('Caveat with allowedCalldata: Bob fails to redeem with disallowed calldata authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenStreaming', { + .addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, startTime, }) - .addCaveat('allowedCalldata', allowedCalldata) + .addCaveat(CaveatType.AllowedCalldata, allowedCalldata) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts index 3c7c9c42..f8739677 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts @@ -105,7 +105,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount: allowance }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount: allowance }) .build(), signature: '0x', }; @@ -177,7 +177,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount: allowance }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount: allowance }) .build(), signature: '0x', }; @@ -392,7 +392,7 @@ test('Utility: getTermsInfo should correctly decode terms from a real delegation authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount, }) .build(), @@ -459,7 +459,7 @@ test('Utility: getSpentAmount should track spending correctly before and after t authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount }) .build(), signature: '0x', }; @@ -579,8 +579,8 @@ test('Caveat with exactCalldata: Bob successfully redeems with exact calldata ma authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount: allowance }) - .addCaveat('exactCalldata', { calldata: exactCalldata }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount: allowance }) + .addCaveat(CaveatType.ExactCalldata, { calldata: exactCalldata }) .build(), signature: '0x', }; @@ -665,8 +665,8 @@ test('Caveat with exactCalldata: Bob fails to redeem with wrong calldata', async authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount: allowance }) - .addCaveat('exactCalldata', { calldata: exactCalldata }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount: allowance }) + .addCaveat(CaveatType.ExactCalldata, { calldata: exactCalldata }) .build(), signature: '0x', }; @@ -724,8 +724,8 @@ test('Caveat with allowedCalldata: Bob successfully redeems with allowed calldat authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount: allowance }) - .addCaveat('allowedCalldata', { startIndex: 0, value: allowedCalldata }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount: allowance }) + .addCaveat(CaveatType.AllowedCalldata, { startIndex: 0, value: allowedCalldata }) .build(), signature: '0x', }; @@ -810,8 +810,8 @@ test('Caveat with allowedCalldata: Bob fails to redeem with disallowed calldata authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(aliceSmartAccount.environment) - .addCaveat('nativeTokenTransferAmount', { maxAmount: allowance }) - .addCaveat('allowedCalldata', { startIndex: 0, value: allowedCalldata }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount: allowance }) + .addCaveat(CaveatType.AllowedCalldata, { startIndex: 0, value: allowedCalldata }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/nonce.test.ts b/packages/delegator-e2e/test/caveats/nonce.test.ts index 9c8681e1..5e8ab13f 100644 --- a/packages/delegator-e2e/test/caveats/nonce.test.ts +++ b/packages/delegator-e2e/test/caveats/nonce.test.ts @@ -11,6 +11,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -123,7 +124,7 @@ const runTest_expectSuccess = async (newCount: bigint, nonce: bigint) => { authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nonce', { nonce: toHex(nonce) }) + .addCaveat(CaveatType.Nonce, { nonce: toHex(nonce) }) .build(), signature: '0x', }; @@ -197,7 +198,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('nonce', { nonce: toHex(nonce) }) + .addCaveat(CaveatType.Nonce, { nonce: toHex(nonce) }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/redeemer.test.ts b/packages/delegator-e2e/test/caveats/redeemer.test.ts index e877ae93..ad43d0a3 100644 --- a/packages/delegator-e2e/test/caveats/redeemer.test.ts +++ b/packages/delegator-e2e/test/caveats/redeemer.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -99,7 +100,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('redeemer', { redeemers: allowedRedeemers }) + .addCaveat(CaveatType.Redeemer, { redeemers: allowedRedeemers }) .build(), signature: '0x', }; @@ -180,7 +181,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('redeemer', { redeemers: allowedRedeemers }) + .addCaveat(CaveatType.Redeemer, { redeemers: allowedRedeemers }) .build(), signature: '0x', }; diff --git a/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts b/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts index 5b6df7a1..ae6a24e2 100644 --- a/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts +++ b/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts @@ -114,7 +114,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('specificActionERC20TransferBatch', { + .addCaveat(CaveatType.SpecificActionERC20TransferBatch, { tokenAddress, recipient, amount, @@ -187,7 +187,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('specificActionERC20TransferBatch', { + .addCaveat(CaveatType.SpecificActionERC20TransferBatch, { tokenAddress, recipient, amount, @@ -665,7 +665,7 @@ describe('SpecificActionERC20TransferBatchEnforcer Contract Read Methods', () => authority: ROOT_AUTHORITY, salt: '0x123', caveats: createCaveatBuilder(environment) - .addCaveat('specificActionERC20TransferBatch', { + .addCaveat(CaveatType.SpecificActionERC20TransferBatch, { tokenAddress: erc20TokenAddress, recipient: bobSmartAccount.address, amount: parseEther('1'), @@ -703,7 +703,7 @@ describe('SpecificActionERC20TransferBatchEnforcer Contract Read Methods', () => authority: ROOT_AUTHORITY, salt: '0x456', caveats: createCaveatBuilder(environment) - .addCaveat('specificActionERC20TransferBatch', { + .addCaveat(CaveatType.SpecificActionERC20TransferBatch, { tokenAddress: erc20TokenAddress, recipient: bobSmartAccount.address, amount: transferAmount, @@ -799,7 +799,7 @@ describe('SpecificActionERC20TransferBatchEnforcer Contract Read Methods', () => authority: ROOT_AUTHORITY, salt: '0x111', caveats: createCaveatBuilder(environment) - .addCaveat('specificActionERC20TransferBatch', { + .addCaveat(CaveatType.SpecificActionERC20TransferBatch, { tokenAddress: erc20TokenAddress, recipient: bobSmartAccount.address, amount: parseEther('1'), @@ -816,7 +816,7 @@ describe('SpecificActionERC20TransferBatchEnforcer Contract Read Methods', () => authority: ROOT_AUTHORITY, salt: '0x222', // Different salt = different delegation hash caveats: createCaveatBuilder(environment) - .addCaveat('specificActionERC20TransferBatch', { + .addCaveat(CaveatType.SpecificActionERC20TransferBatch, { tokenAddress: erc20TokenAddress, recipient: bobSmartAccount.address, amount: parseEther('1'), diff --git a/packages/delegator-e2e/test/caveats/timestamp.test.ts b/packages/delegator-e2e/test/caveats/timestamp.test.ts index bfb349f8..7a9e51af 100644 --- a/packages/delegator-e2e/test/caveats/timestamp.test.ts +++ b/packages/delegator-e2e/test/caveats/timestamp.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -160,7 +161,7 @@ const runTest_expectSuccess = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('timestamp', { + .addCaveat(CaveatType.Timestamp, { afterThreshold, beforeThreshold, }) @@ -245,7 +246,7 @@ const runTest_expectFailure = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('timestamp', { + .addCaveat(CaveatType.Timestamp, { afterThreshold, beforeThreshold, }) diff --git a/packages/delegator-e2e/test/caveats/valueLte.test.ts b/packages/delegator-e2e/test/caveats/valueLte.test.ts index 8fb4b130..d714ea01 100644 --- a/packages/delegator-e2e/test/caveats/valueLte.test.ts +++ b/packages/delegator-e2e/test/caveats/valueLte.test.ts @@ -10,6 +10,7 @@ import { } from '@metamask/smart-accounts-kit'; import { createCaveatBuilder, + CaveatType, encodeExecutionCalldatas, encodeDelegations, } from '@metamask/smart-accounts-kit/utils'; @@ -125,7 +126,7 @@ const submitUserOperationForTest = async ( authority: ROOT_AUTHORITY, salt: '0x0', caveats: createCaveatBuilder(environment) - .addCaveat('valueLte', { maxValue }) + .addCaveat(CaveatType.ValueLte, { maxValue }) .build(), signature: '0x', }; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/allowedCalldataBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/allowedCalldataBuilder.ts index fa565c16..22ed02ef 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/allowedCalldataBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/allowedCalldataBuilder.ts @@ -2,8 +2,9 @@ import { createAllowedCalldataTerms } from '@metamask/delegation-core'; import { type Hex } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const allowedCalldata = 'allowedCalldata'; +export const allowedCalldata = CaveatType.AllowedCalldata; export type AllowedCalldataBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/allowedMethodsBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/allowedMethodsBuilder.ts index d9a1cfc8..af061486 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/allowedMethodsBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/allowedMethodsBuilder.ts @@ -3,8 +3,9 @@ import { isHex, toFunctionSelector } from 'viem'; import type { AbiFunction, Hex } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const allowedMethods = 'allowedMethods'; +export const allowedMethods = CaveatType.AllowedMethods; export type MethodSelector = Hex | string | AbiFunction; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/allowedTargetsBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/allowedTargetsBuilder.ts index fcd1e15c..835c8837 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/allowedTargetsBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/allowedTargetsBuilder.ts @@ -2,8 +2,9 @@ import { createAllowedTargetsTerms } from '@metamask/delegation-core'; import { isAddress, type Address } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const allowedTargets = 'allowedTargets'; +export const allowedTargets = CaveatType.AllowedTargets; export type AllowedTargetsBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/argsEqualityCheckBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/argsEqualityCheckBuilder.ts index 0234e408..2b8072f5 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/argsEqualityCheckBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/argsEqualityCheckBuilder.ts @@ -2,8 +2,9 @@ import { createArgsEqualityCheckTerms } from '@metamask/delegation-core'; import { type Hex, isHex } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const argsEqualityCheck = 'argsEqualityCheck'; +export const argsEqualityCheck = CaveatType.ArgsEqualityCheck; export type ArgsEqualityCheckBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/blockNumberBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/blockNumberBuilder.ts index 99d3e033..4d4bfa7d 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/blockNumberBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/blockNumberBuilder.ts @@ -1,8 +1,9 @@ import { createBlockNumberTerms } from '@metamask/delegation-core'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const blockNumber = 'blockNumber'; +export const blockNumber = CaveatType.BlockNumber; export type BlockNumberBuilderConfig = { /** 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..027589df --- /dev/null +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts @@ -0,0 +1,38 @@ +/** + * 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', +} diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 0a5f377f..659b5974 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -12,6 +12,7 @@ import { import { blockNumber, blockNumberBuilder } from './blockNumberBuilder'; import type { CaveatBuilderConfig } from './caveatBuilder'; import { CaveatBuilder } from './caveatBuilder'; +import type { CaveatType } from './caveatType'; import { deployed, deployedBuilder } from './deployedBuilder'; import { erc1155BalanceChange, @@ -84,45 +85,6 @@ import { import { timestamp, timestampBuilder } from './timestampBuilder'; import { valueLte, valueLteBuilder } from './valueLteBuilder'; -/** - * 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', -} - // While we could derive CoreCaveatMap from the createCaveatBuilder function, // doing so would significantly complicate type resolution. By explicitly // declaring the return type of createCaveatBuilder, we ensure the caveat @@ -168,6 +130,9 @@ type CoreCaveatMap = { */ export type CoreCaveatBuilder = CaveatBuilder; +// Re-export CaveatType for convenience +export { CaveatType } from './caveatType'; + // We want to allow the caveat `type` to be passed as either an enum reference, // or the enum's string value. This generic accepts a union of caveat configs, and // converts them to an identical union except the `type` parameter is converted diff --git a/packages/smart-accounts-kit/src/caveatBuilder/deployedBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/deployedBuilder.ts index e1a2d358..5eddd1b7 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/deployedBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/deployedBuilder.ts @@ -2,8 +2,9 @@ import { createDeployedTerms } from '@metamask/delegation-core'; import { isAddress, isHex, type Address, type Hex } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const deployed = 'deployed'; +export const deployed = CaveatType.Deployed; export type DeployedBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc1155BalanceChangeBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc1155BalanceChangeBuilder.ts index c3746a4e..7c00d6e4 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc1155BalanceChangeBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc1155BalanceChangeBuilder.ts @@ -2,9 +2,10 @@ import { createERC1155BalanceChangeTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; import { BalanceChangeType } from './types'; -export const erc1155BalanceChange = 'erc1155BalanceChange'; +export const erc1155BalanceChange = CaveatType.Erc1155BalanceChange; export type Erc1155BalanceChangeBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc20BalanceChangeBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc20BalanceChangeBuilder.ts index 5dcc982c..fe46dd45 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc20BalanceChangeBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc20BalanceChangeBuilder.ts @@ -2,9 +2,10 @@ import { createERC20BalanceChangeTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; import { BalanceChangeType } from './types'; -export const erc20BalanceChange = 'erc20BalanceChange'; +export const erc20BalanceChange = CaveatType.Erc20BalanceChange; export type Erc20BalanceChangeBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc20PeriodTransferBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc20PeriodTransferBuilder.ts index e63aea9c..83087a70 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc20PeriodTransferBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc20PeriodTransferBuilder.ts @@ -2,8 +2,9 @@ import { createERC20TokenPeriodTransferTerms } from '@metamask/delegation-core'; import type { Address } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const erc20PeriodTransfer = 'erc20PeriodTransfer'; +export const erc20PeriodTransfer = CaveatType.Erc20PeriodTransfer; export type Erc20PeriodTransferBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc20StreamingBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc20StreamingBuilder.ts index 1b5db204..e0c1b804 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc20StreamingBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc20StreamingBuilder.ts @@ -2,8 +2,9 @@ import { createERC20StreamingTerms } from '@metamask/delegation-core'; import { type Address } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const erc20Streaming = 'erc20Streaming'; +export const erc20Streaming = CaveatType.Erc20Streaming; export type Erc20StreamingBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc20TransferAmountBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc20TransferAmountBuilder.ts index ca2f2563..74cddb9b 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc20TransferAmountBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc20TransferAmountBuilder.ts @@ -3,8 +3,9 @@ import type { Address } from 'viem'; import { isAddress } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const erc20TransferAmount = 'erc20TransferAmount'; +export const erc20TransferAmount = CaveatType.Erc20TransferAmount; export type Erc20TransferAmountBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc721BalanceChangeBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc721BalanceChangeBuilder.ts index 1509707d..5541ceeb 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc721BalanceChangeBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc721BalanceChangeBuilder.ts @@ -2,9 +2,10 @@ import { createERC721BalanceChangeTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; import { BalanceChangeType } from './types'; -export const erc721BalanceChange = 'erc721BalanceChange'; +export const erc721BalanceChange = CaveatType.Erc721BalanceChange; export type Erc721BalanceChangeBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/erc721TransferBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/erc721TransferBuilder.ts index 96ad9184..36f1547f 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/erc721TransferBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/erc721TransferBuilder.ts @@ -2,8 +2,9 @@ import { createERC721TransferTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const erc721Transfer = 'erc721Transfer'; +export const erc721Transfer = CaveatType.Erc721Transfer; export type Erc721TransferBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBatchBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBatchBuilder.ts index 0b294f32..70550853 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBatchBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBatchBuilder.ts @@ -3,8 +3,9 @@ import { isAddress } from 'viem'; import type { ExecutionStruct } from '../executions'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const exactCalldataBatch = 'exactCalldataBatch'; +export const exactCalldataBatch = CaveatType.ExactCalldataBatch; export type ExactCalldataBatchBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBuilder.ts index 85140d3f..ad0b5234 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/exactCalldataBuilder.ts @@ -2,8 +2,9 @@ import { createExactCalldataTerms } from '@metamask/delegation-core'; import type { Hex } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const exactCalldata = 'exactCalldata'; +export const exactCalldata = CaveatType.ExactCalldata; export type ExactCalldataBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBatchBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBatchBuilder.ts index cf15aead..efdf3c65 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBatchBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBatchBuilder.ts @@ -3,8 +3,9 @@ import { isAddress } from 'viem'; import type { ExecutionStruct } from '../executions'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const exactExecutionBatch = 'exactExecutionBatch'; +export const exactExecutionBatch = CaveatType.ExactExecutionBatch; export type ExactExecutionBatchBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBuilder.ts index 4a879661..3ec81c95 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/exactExecutionBuilder.ts @@ -3,8 +3,9 @@ import { isAddress } from 'viem'; import type { ExecutionStruct } from '../executions'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const exactExecution = 'exactExecution'; +export const exactExecution = CaveatType.ExactExecution; export type ExactExecutionBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/idBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/idBuilder.ts index c1b769a3..f527ca3f 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/idBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/idBuilder.ts @@ -2,6 +2,7 @@ import { createIdTerms } from '@metamask/delegation-core'; import { maxUint256 } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; export type IdBuilderConfig = { /** @@ -10,7 +11,7 @@ export type IdBuilderConfig = { id: bigint | number; }; -export const id = 'id'; +export const id = CaveatType.Id; /** * Builds a caveat struct for the IdEnforcer. diff --git a/packages/smart-accounts-kit/src/caveatBuilder/limitedCallsBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/limitedCallsBuilder.ts index eb71fa5c..db76d364 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/limitedCallsBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/limitedCallsBuilder.ts @@ -1,8 +1,9 @@ import { createLimitedCallsTerms } from '@metamask/delegation-core'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const limitedCalls = 'limitedCalls'; +export const limitedCalls = CaveatType.LimitedCalls; export type LimitedCallsBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/multiTokenPeriodBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/multiTokenPeriodBuilder.ts index 32b90447..79457ee7 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/multiTokenPeriodBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/multiTokenPeriodBuilder.ts @@ -3,6 +3,7 @@ import type { Hex } from 'viem'; import { isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; export type TokenPeriodConfig = { /** @@ -27,7 +28,7 @@ export type MultiTokenPeriodBuilderConfig = { tokenConfigs: TokenPeriodConfig[]; }; -export const multiTokenPeriod = 'multiTokenPeriod'; +export const multiTokenPeriod = CaveatType.MultiTokenPeriod; /** * Creates a caveat for the MultiTokenPeriodEnforcer. diff --git a/packages/smart-accounts-kit/src/caveatBuilder/nativeBalanceChangeBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/nativeBalanceChangeBuilder.ts index 66fc8897..4a1c5646 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/nativeBalanceChangeBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/nativeBalanceChangeBuilder.ts @@ -2,9 +2,10 @@ import { createNativeBalanceChangeTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; import { BalanceChangeType } from './types'; -export const nativeBalanceChange = 'nativeBalanceChange'; +export const nativeBalanceChange = CaveatType.NativeBalanceChange; export type NativeBalanceChangeBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPaymentBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPaymentBuilder.ts index 53e81b53..ef53efe7 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPaymentBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPaymentBuilder.ts @@ -2,8 +2,9 @@ import { createNativeTokenPaymentTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const nativeTokenPayment = 'nativeTokenPayment'; +export const nativeTokenPayment = CaveatType.NativeTokenPayment; export type NativeTokenPaymentBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPeriodTransferBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPeriodTransferBuilder.ts index 5b202a0c..4e976948 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPeriodTransferBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenPeriodTransferBuilder.ts @@ -1,8 +1,9 @@ import { createNativeTokenPeriodTransferTerms } from '@metamask/delegation-core'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const nativeTokenPeriodTransfer = 'nativeTokenPeriodTransfer'; +export const nativeTokenPeriodTransfer = CaveatType.NativeTokenPeriodTransfer; export type NativeTokenPeriodTransferBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenStreamingBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenStreamingBuilder.ts index 0530512b..1943661c 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenStreamingBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenStreamingBuilder.ts @@ -1,8 +1,9 @@ import { createNativeTokenStreamingTerms } from '@metamask/delegation-core'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const nativeTokenStreaming = 'nativeTokenStreaming'; +export const nativeTokenStreaming = CaveatType.NativeTokenStreaming; export type NativeTokenStreamingBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenTransferAmountBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenTransferAmountBuilder.ts index 373e0229..4e2c8634 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenTransferAmountBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/nativeTokenTransferAmountBuilder.ts @@ -1,8 +1,9 @@ import { createNativeTokenTransferAmountTerms } from '@metamask/delegation-core'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const nativeTokenTransferAmount = 'nativeTokenTransferAmount'; +export const nativeTokenTransferAmount = CaveatType.NativeTokenTransferAmount; export type NativeTokenTransferAmountBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/nonceBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/nonceBuilder.ts index 1d7da0d0..af898292 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/nonceBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/nonceBuilder.ts @@ -2,8 +2,9 @@ import { createNonceTerms } from '@metamask/delegation-core'; import { type Hex } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const nonce = 'nonce'; +export const nonce = CaveatType.Nonce; export type NonceBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/ownershipTransferBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/ownershipTransferBuilder.ts index 7a5de773..a7f1d956 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/ownershipTransferBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/ownershipTransferBuilder.ts @@ -2,8 +2,9 @@ import { createOwnershipTransferTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { SmartAccountsEnvironment, Caveat } from '../types'; +import { CaveatType } from './caveatType'; -export const ownershipTransfer = 'ownershipTransfer'; +export const ownershipTransfer = CaveatType.OwnershipTransfer; export type OwnershipTransferBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/redeemerBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/redeemerBuilder.ts index 23995549..dc5b552a 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/redeemerBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/redeemerBuilder.ts @@ -2,8 +2,9 @@ import { createRedeemerTerms } from '@metamask/delegation-core'; import { type Address, isAddress } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const redeemer = 'redeemer'; +export const redeemer = CaveatType.Redeemer; export type RedeemerBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts index b7ca0df0..0c3c9af6 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts @@ -1,4 +1,3 @@ -import { validateCaveatType } from '../utils'; import type { CaveatBuilder } from './caveatBuilder'; import type { CoreCaveatConfiguration } from './coreCaveatBuilder'; import { createCaveatBuilderFromScope, type ScopeConfig } from './scope'; @@ -36,8 +35,8 @@ export const resolveCaveats = ({ try { if ('type' in caveat) { const { type, ...config } = caveat; - validateCaveatType(type); - scopeCaveatBuilder.addCaveat(type, config); + // Type assertion: addCaveat validates at runtime and throws if enforcer doesn't exist + (scopeCaveatBuilder.addCaveat as any)(type, config); } else { scopeCaveatBuilder.addCaveat(caveat); } diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20PeriodicScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20PeriodicScope.ts index 9f1a1fdc..ab8ee51d 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20PeriodicScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20PeriodicScope.ts @@ -1,6 +1,6 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { Erc20PeriodTransferBuilderConfig } from '../erc20PeriodTransferBuilder'; @@ -22,10 +22,10 @@ export function createErc20PeriodicCaveatBuilder( config: Erc20PeriodicScopeConfig, ): CoreCaveatBuilder { return createCaveatBuilder(environment) - .addCaveat('valueLte', { + .addCaveat(CaveatType.ValueLte, { maxValue: 0n, }) - .addCaveat('erc20PeriodTransfer', { + .addCaveat(CaveatType.Erc20PeriodTransfer, { tokenAddress: config.tokenAddress, periodAmount: config.periodAmount, periodDuration: config.periodDuration, diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20StreamingScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20StreamingScope.ts index b333185d..35977442 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20StreamingScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20StreamingScope.ts @@ -1,6 +1,6 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { Erc20StreamingBuilderConfig } from '../erc20StreamingBuilder'; @@ -22,10 +22,10 @@ export function createErc20StreamingCaveatBuilder( config: Erc20StreamingScopeConfig, ): CoreCaveatBuilder { return createCaveatBuilder(environment) - .addCaveat('valueLte', { + .addCaveat(CaveatType.ValueLte, { maxValue: 0n, }) - .addCaveat('erc20Streaming', { + .addCaveat(CaveatType.Erc20Streaming, { tokenAddress: config.tokenAddress, initialAmount: config.initialAmount, maxAmount: config.maxAmount, diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20TransferScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20TransferScope.ts index fbb26b1f..23b24770 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20TransferScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc20TransferScope.ts @@ -1,6 +1,6 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { Erc20TransferAmountBuilderConfig } from '../erc20TransferAmountBuilder'; @@ -22,10 +22,10 @@ export function createErc20TransferCaveatBuilder( config: Erc20TransferScopeConfig, ): CoreCaveatBuilder { return createCaveatBuilder(environment) - .addCaveat('valueLte', { + .addCaveat(CaveatType.ValueLte, { maxValue: 0n, }) - .addCaveat('erc20TransferAmount', { + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress: config.tokenAddress, maxAmount: config.maxAmount, }); diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc721Scope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc721Scope.ts index 19e1b9fc..a7687c0e 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/erc721Scope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/erc721Scope.ts @@ -1,7 +1,7 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; import { hasProperties } from '../../utils'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { Erc721TransferBuilderConfig } from '../erc721TransferBuilder'; @@ -38,7 +38,7 @@ export function createErc721CaveatBuilder( } const caveatBuilder = createCaveatBuilder(environment).addCaveat( - 'erc721Transfer', + CaveatType.Erc721Transfer, config, ); diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/functionCallScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/functionCallScope.ts index 6bb0c4cc..f9cedeb5 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/functionCallScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/functionCallScope.ts @@ -4,7 +4,7 @@ import { hasProperties } from '../../utils'; import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder'; import type { AllowedMethodsBuilderConfig } from '../allowedMethodsBuilder'; import type { AllowedTargetsBuilderConfig } from '../allowedTargetsBuilder'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder'; import type { ValueLteBuilderConfig } from '../valueLteBuilder'; @@ -54,16 +54,16 @@ export function createFunctionCallCaveatBuilder( const valueLteConfig = config.valueLte ?? { maxValue: 0n }; const caveatBuilder = createCaveatBuilder(environment) - .addCaveat('allowedTargets', { targets }) - .addCaveat('allowedMethods', { selectors }) - .addCaveat('valueLte', valueLteConfig); + .addCaveat(CaveatType.AllowedTargets, { targets }) + .addCaveat(CaveatType.AllowedMethods, { selectors }) + .addCaveat(CaveatType.ValueLte, valueLteConfig); if (allowedCalldata && allowedCalldata.length > 0) { allowedCalldata.forEach((calldataConfig) => { - caveatBuilder.addCaveat('allowedCalldata', calldataConfig); + caveatBuilder.addCaveat(CaveatType.AllowedCalldata, calldataConfig); }); } else if (exactCalldata) { - caveatBuilder.addCaveat('exactCalldata', exactCalldata); + caveatBuilder.addCaveat(CaveatType.ExactCalldata, exactCalldata); } return caveatBuilder; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenPeriodicScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenPeriodicScope.ts index d72daaa6..03259865 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenPeriodicScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenPeriodicScope.ts @@ -1,7 +1,7 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder'; import type { NativeTokenPeriodTransferBuilderConfig } from '../nativeTokenPeriodTransferBuilder'; @@ -45,19 +45,19 @@ export function createNativeTokenPeriodicCaveatBuilder( // Add calldata restrictions if (allowedCalldata && allowedCalldata.length > 0) { allowedCalldata.forEach((calldataConfig) => { - caveatBuilder.addCaveat('allowedCalldata', calldataConfig); + caveatBuilder.addCaveat(CaveatType.AllowedCalldata, calldataConfig); }); } else if (exactCalldata) { - caveatBuilder.addCaveat('exactCalldata', exactCalldata); + caveatBuilder.addCaveat(CaveatType.ExactCalldata, exactCalldata); } else { // Default behavior: only allow empty calldata - caveatBuilder.addCaveat('exactCalldata', { + caveatBuilder.addCaveat(CaveatType.ExactCalldata, { calldata: '0x', }); } // Add native token period transfer restriction - caveatBuilder.addCaveat('nativeTokenPeriodTransfer', { + caveatBuilder.addCaveat(CaveatType.NativeTokenPeriodTransfer, { periodAmount, periodDuration, startDate, diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenStreamingScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenStreamingScope.ts index 3139bab3..74453180 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenStreamingScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenStreamingScope.ts @@ -1,7 +1,7 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder'; import type { NativeTokenStreamingBuilderConfig } from '../nativeTokenStreamingBuilder'; @@ -46,19 +46,19 @@ export function createNativeTokenStreamingCaveatBuilder( // Add calldata restrictions if (allowedCalldata && allowedCalldata.length > 0) { allowedCalldata.forEach((calldataConfig) => { - caveatBuilder.addCaveat('allowedCalldata', calldataConfig); + caveatBuilder.addCaveat(CaveatType.AllowedCalldata, calldataConfig); }); } else if (exactCalldata) { - caveatBuilder.addCaveat('exactCalldata', exactCalldata); + caveatBuilder.addCaveat(CaveatType.ExactCalldata, exactCalldata); } else { // Default behavior: only allow empty calldata - caveatBuilder.addCaveat('exactCalldata', { + caveatBuilder.addCaveat(CaveatType.ExactCalldata, { calldata: '0x', }); } // Add native token streaming restriction - caveatBuilder.addCaveat('nativeTokenStreaming', { + caveatBuilder.addCaveat(CaveatType.NativeTokenStreaming, { initialAmount, maxAmount, amountPerSecond, diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenTransferScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenTransferScope.ts index 82618526..d2fb82de 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenTransferScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/nativeTokenTransferScope.ts @@ -1,7 +1,7 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder'; import type { NativeTokenTransferAmountBuilderConfig } from '../nativeTokenTransferAmountBuilder'; @@ -39,19 +39,19 @@ export function createNativeTokenTransferCaveatBuilder( // Add calldata restrictions if (allowedCalldata && allowedCalldata.length > 0) { allowedCalldata.forEach((calldataConfig) => { - caveatBuilder.addCaveat('allowedCalldata', calldataConfig); + caveatBuilder.addCaveat(CaveatType.AllowedCalldata, calldataConfig); }); } else if (exactCalldata) { - caveatBuilder.addCaveat('exactCalldata', exactCalldata); + caveatBuilder.addCaveat(CaveatType.ExactCalldata, exactCalldata); } else { // Default behavior: only allow empty calldata - caveatBuilder.addCaveat('exactCalldata', { + caveatBuilder.addCaveat(CaveatType.ExactCalldata, { calldata: '0x', }); } // Add native token transfer amount restriction - caveatBuilder.addCaveat('nativeTokenTransferAmount', { + caveatBuilder.addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount, }); diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/ownershipScope.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/ownershipScope.ts index 2033d708..5b7450ed 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/ownershipScope.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/ownershipScope.ts @@ -1,7 +1,7 @@ import type { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; import { hasProperties } from '../../utils'; -import { createCaveatBuilder } from '../coreCaveatBuilder'; +import { CaveatType, createCaveatBuilder } from '../coreCaveatBuilder'; import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; import type { OwnershipTransferBuilderConfig } from '../ownershipTransferBuilder'; @@ -38,7 +38,7 @@ export function createOwnershipCaveatBuilder( } const caveatBuilder = createCaveatBuilder(environment).addCaveat( - 'ownershipTransfer', + CaveatType.OwnershipTransfer, config, ); diff --git a/packages/smart-accounts-kit/src/caveatBuilder/specificActionERC20TransferBatchBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/specificActionERC20TransferBatchBuilder.ts index e32f7bcd..a0f51d7b 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/specificActionERC20TransferBatchBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/specificActionERC20TransferBatchBuilder.ts @@ -2,9 +2,10 @@ import { createSpecificActionERC20TransferBatchTerms } from '@metamask/delegatio import { isAddress, type Address, type Hex } from 'viem'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; export const specificActionERC20TransferBatch = - 'specificActionERC20TransferBatch'; + CaveatType.SpecificActionERC20TransferBatch; export type SpecificActionErc20TransferBatchBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/timestampBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/timestampBuilder.ts index 5e44ea46..6642accc 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/timestampBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/timestampBuilder.ts @@ -1,8 +1,9 @@ import { createTimestampTerms } from '@metamask/delegation-core'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const timestamp = 'timestamp'; +export const timestamp = CaveatType.Timestamp; export type TimestampBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/caveatBuilder/valueLteBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/valueLteBuilder.ts index 2031c31f..7cf095d1 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/valueLteBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/valueLteBuilder.ts @@ -1,8 +1,9 @@ import { createValueLteTerms } from '@metamask/delegation-core'; import type { Caveat, SmartAccountsEnvironment } from '../types'; +import { CaveatType } from './caveatType'; -export const valueLte = 'valueLte'; +export const valueLte = CaveatType.ValueLte; export type ValueLteBuilderConfig = { /** diff --git a/packages/smart-accounts-kit/src/utils.ts b/packages/smart-accounts-kit/src/utils.ts index 3680501a..14f07b91 100644 --- a/packages/smart-accounts-kit/src/utils.ts +++ b/packages/smart-accounts-kit/src/utils.ts @@ -1,26 +1,5 @@ import { type Hex, isHex, toHex } from 'viem'; -import { CaveatType } from './caveatBuilder/coreCaveatBuilder'; - -/** - * Validates that a caveat type is a recognized CaveatType enum value. - * Only validates against standard CaveatType enum values. - * Custom caveat types registered via CaveatBuilder.extend() bypass this validation. - * - * @param type - The caveat type to validate (either enum value or string). - * @throws {Error} If the caveat type is not a recognized CaveatType enum value. - */ -export function validateCaveatType( - type: string | CaveatType, -): asserts type is CaveatType { - const validTypes = Object.values(CaveatType); - if (!validTypes.includes(type as CaveatType)) { - throw new Error( - `Invalid caveat type: "${type}". Valid types are: ${validTypes.join(', ')}`, - ); - } -} - /** * Checks if two hexadecimal strings are equal, ignoring case sensitivity. * diff --git a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts index b5882690..982b94ef 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts @@ -140,7 +140,7 @@ describe('CaveatType enum usage patterns', () => { .build(); const stringResult = builderWithString - .addCaveat('allowedMethods', { selectors }) + .addCaveat(CaveatType.AllowedMethods, { selectors }) .build(); // Both should produce the same result @@ -409,11 +409,11 @@ describe('CaveatType enum usage patterns', () => { const builder = createCaveatBuilder(environment); // This should also compile without errors - builder.addCaveat('allowedMethods', { + builder.addCaveat(CaveatType.AllowedMethods, { selectors: ['0x12345678'], }); - builder.addCaveat('valueLte', { maxValue: 1000n }); - builder.addCaveat('allowedTargets', { + builder.addCaveat(CaveatType.ValueLte, { maxValue: 1000n }); + builder.addCaveat(CaveatType.AllowedTargets, { targets: [randomAddress()], }); diff --git a/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts index 7006e38f..bed5b36b 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts @@ -2,7 +2,11 @@ import { concat, encodePacked, isAddress, pad, toHex } from 'viem'; import type { Address } from 'viem/accounts'; import { expect, describe, it } from 'vitest'; -import { createCaveatBuilder, CaveatBuilder } from '../../src/caveatBuilder'; +import { + createCaveatBuilder, + CaveatBuilder, + CaveatType, +} from '../../src/caveatBuilder'; import { BalanceChangeType } from '../../src/caveatBuilder/types'; import type { SmartAccountsEnvironment } from '../../src/types'; import { randomAddress, randomBytes } from '../utils'; @@ -61,7 +65,7 @@ describe('createCaveatBuilder()', () => { const selectors = [randomBytes(4), randomBytes(4)]; const caveats = builder - .addCaveat('allowedMethods', { selectors }) + .addCaveat(CaveatType.AllowedMethods, { selectors }) .build(); expect(caveats).to.deep.equal([ @@ -84,7 +88,9 @@ describe('createCaveatBuilder()', () => { const targets: [Address, Address] = [randomAddress(), randomAddress()]; - const caveats = builder.addCaveat('allowedTargets', { targets }).build(); + const caveats = builder + .addCaveat(CaveatType.AllowedTargets, { targets }) + .build(); expect(caveats).to.deep.equal([ { @@ -110,7 +116,7 @@ describe('createCaveatBuilder()', () => { const bytecode = randomBytes(256); const caveats = builder - .addCaveat('deployed', { contractAddress, salt, bytecode }) + .addCaveat(CaveatType.Deployed, { contractAddress, salt, bytecode }) .build(); expect(caveats).to.deep.equal([ @@ -136,7 +142,7 @@ describe('createCaveatBuilder()', () => { const startIndex = Math.floor(Math.random() * 2 ** 32); const caveats = builder - .addCaveat('allowedCalldata', { startIndex, value }) + .addCaveat(CaveatType.AllowedCalldata, { startIndex, value }) .build(); expect(caveats).to.deep.equal([ @@ -165,7 +171,7 @@ describe('createCaveatBuilder()', () => { ); const caveats = builder - .addCaveat('erc20BalanceChange', { + .addCaveat(CaveatType.Erc20BalanceChange, { tokenAddress, recipient, balance, @@ -198,7 +204,9 @@ describe('createCaveatBuilder()', () => { Math.floor(Math.random() * Number.MAX_SAFE_INTEGER), ); - const caveats = builder.addCaveat('valueLte', { maxValue }).build(); + const caveats = builder + .addCaveat(CaveatType.ValueLte, { maxValue }) + .build(); expect(caveats).to.deep.equal([ { @@ -219,7 +227,9 @@ describe('createCaveatBuilder()', () => { const builder = createCaveatBuilder(environment); const limit = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); - const caveats = builder.addCaveat('limitedCalls', { limit }).build(); + const caveats = builder + .addCaveat(CaveatType.LimitedCalls, { limit }) + .build(); expect(caveats).to.deep.equal([ { @@ -240,7 +250,7 @@ describe('createCaveatBuilder()', () => { const builder = createCaveatBuilder(environment); const idValue = BigInt(Math.floor(Math.random() * 2 ** 32)); - const caveats = builder.addCaveat('id', { id: idValue }).build(); + const caveats = builder.addCaveat(CaveatType.Id, { id: idValue }).build(); expect(caveats).to.deep.equal([ { @@ -261,7 +271,7 @@ describe('createCaveatBuilder()', () => { const builder = createCaveatBuilder(environment); const nonce = randomBytes(16); - const caveats = builder.addCaveat('nonce', { nonce }).build(); + const caveats = builder.addCaveat(CaveatType.Nonce, { nonce }).build(); expect(caveats).to.deep.equal([ { @@ -285,7 +295,7 @@ describe('createCaveatBuilder()', () => { const beforeThreshold = 2000; const caveats = builder - .addCaveat('timestamp', { + .addCaveat(CaveatType.Timestamp, { afterThreshold, beforeThreshold, }) @@ -316,7 +326,7 @@ describe('createCaveatBuilder()', () => { const beforeThreshold = 2000n; const caveats = builder - .addCaveat('blockNumber', { + .addCaveat(CaveatType.BlockNumber, { afterThreshold, beforeThreshold, }) @@ -345,7 +355,7 @@ describe('createCaveatBuilder()', () => { const maxAmount = 1000000000000000000n; // 1 ETH in wei const caveats = builder - .addCaveat('nativeTokenTransferAmount', { maxAmount }) + .addCaveat(CaveatType.NativeTokenTransferAmount, { maxAmount }) .build(); expect(caveats).to.deep.equal([ @@ -371,7 +381,7 @@ describe('createCaveatBuilder()', () => { const minBalance = 500000000000000000n; // 0.5 ETH in wei const caveats = builder - .addCaveat('nativeBalanceChange', { + .addCaveat(CaveatType.NativeBalanceChange, { recipient, balance: minBalance, changeType: BalanceChangeType.Increase, @@ -402,7 +412,7 @@ describe('createCaveatBuilder()', () => { const recipient = randomAddress('lowercase'); const caveats = builder - .addCaveat('nativeTokenPayment', { recipient, amount }) + .addCaveat(CaveatType.NativeTokenPayment, { recipient, amount }) .build(); expect(caveats).to.deep.equal([ @@ -427,7 +437,7 @@ describe('createCaveatBuilder()', () => { const maxAmount = 2000n; const caveats = builder - .addCaveat('erc20TransferAmount', { tokenAddress, maxAmount }) + .addCaveat(CaveatType.Erc20TransferAmount, { tokenAddress, maxAmount }) .build(); expect(caveats).to.deep.equal([ @@ -451,7 +461,7 @@ describe('createCaveatBuilder()', () => { const redeemerAddress = randomAddress(); const caveats = builder - .addCaveat('redeemer', { redeemers: [redeemerAddress] }) + .addCaveat(CaveatType.Redeemer, { redeemers: [redeemerAddress] }) .build(); expect(caveats).to.deep.equal([ @@ -473,7 +483,9 @@ describe('createCaveatBuilder()', () => { const builder = createCaveatBuilder(environment); const args = '0x1234567890'; - const caveats = builder.addCaveat('argsEqualityCheck', { args }).build(); + const caveats = builder + .addCaveat(CaveatType.ArgsEqualityCheck, { args }) + .build(); expect(caveats).to.deep.equal([ { diff --git a/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts index 871952bc..5753b703 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts @@ -2,7 +2,10 @@ import { describe, it, expect } from 'vitest'; import { CaveatBuilder } from '../../src/caveatBuilder/caveatBuilder'; import type { CoreCaveatConfiguration } from '../../src/caveatBuilder/coreCaveatBuilder'; -import { createCaveatBuilder } from '../../src/caveatBuilder/coreCaveatBuilder'; +import { + createCaveatBuilder, + CaveatType, +} from '../../src/caveatBuilder/coreCaveatBuilder'; import { resolveCaveats } from '../../src/caveatBuilder/resolveCaveats'; import type { ScopeConfig } from '../../src/caveatBuilder/scope'; import { ScopeType } from '../../src/constants'; @@ -75,7 +78,7 @@ describe('resolveCaveats', () => { const caveatBuilder = createCaveatBuilder(environment, { allowInsecureUnrestrictedDelegation: true, }); - caveatBuilder.addCaveat('allowedMethods', { + caveatBuilder.addCaveat(CaveatType.AllowedMethods, { selectors: ['0x12345678'], }); @@ -237,7 +240,7 @@ describe('resolveCaveats', () => { scope: erc20Scope, caveats: [invalidType as any], }); - }).to.throw('Invalid caveat'); + }).to.throw('does not exist'); }); }); }); From 540f6494c500fb12b00633b1ac87602c14f54991 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Fri, 27 Feb 2026 18:22:26 +1300 Subject: [PATCH 09/18] refactor: export CaveatType from caveatBuilder for improved type access --- packages/smart-accounts-kit/src/utils/index.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/smart-accounts-kit/src/utils/index.ts b/packages/smart-accounts-kit/src/utils/index.ts index 6ed7c103..2fcf2d9c 100644 --- a/packages/smart-accounts-kit/src/utils/index.ts +++ b/packages/smart-accounts-kit/src/utils/index.ts @@ -42,4 +42,8 @@ export { export type { CoreCaveatBuilder, CaveatBuilderConfig } from '../caveatBuilder'; -export { createCaveatBuilder, CaveatBuilder } from '../caveatBuilder'; +export { + createCaveatBuilder, + CaveatBuilder, + CaveatType, +} from '../caveatBuilder'; From e688a1427376bc2bf5f608169369878c14154be5 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Fri, 27 Feb 2026 18:50:09 +1300 Subject: [PATCH 10/18] refactor: unify config conversion utilities by using ConvertEnumConfigToInputs in scope types --- .../src/caveatBuilder/coreCaveatBuilder.ts | 23 ++++++++++--------- .../src/caveatBuilder/scope/index.ts | 15 ++++++------ 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 659b5974..d1aa7347 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -133,18 +133,19 @@ export type CoreCaveatBuilder = CaveatBuilder; // Re-export CaveatType for convenience export { CaveatType } from './caveatType'; -// We want to allow the caveat `type` to be passed as either an enum reference, -// or the enum's string value. This generic accepts a union of caveat configs, and -// converts them to an identical union except the `type` parameter is converted -// to a union of `CaveatType.XXXX | `${CaveatType.XXXX}`. -// We preserve the discriminated union pattern by using T['type'] instead of -// expanding to the full CaveatType enum, which ensures TypeScript can narrow -// by the type field. -export type ConvertCaveatConfigsToInputs = - T extends { type: string } - ? Omit & { type: T['type'] | `${T['type']}` } +// Shared utility for allowing enum or string value for discriminated union types. +// This converts a config type to accept both the enum value and its string representation. +// We preserve the discriminated union pattern by using TConfig['type'] instead of +// expanding to the full enum, which ensures TypeScript can narrow by the type field. +export type ConvertEnumConfigToInputs = + TConfig extends { type: string } + ? Omit & { type: TConfig['type'] | `${TConfig['type']}` } : never; +// Backward compatibility alias for caveat configs +export type ConvertCaveatConfigsToInputs = + ConvertEnumConfigToInputs; + type ExtractCaveatMapType> = TCaveatBuilder extends CaveatBuilder ? TCaveatMap : never; type ExtractedCoreMap = ExtractCaveatMapType; @@ -160,7 +161,7 @@ export type CaveatConfiguration< CaveatMap = ExtractCaveatMapType, > = CaveatMap extends Record any> - ? ConvertCaveatConfigsToInputs< + ? ConvertEnumConfigToInputs< { [TType in keyof CaveatMap]: { type: TType extends string ? TType : never; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts index a53debb1..a0155dc9 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts @@ -36,16 +36,15 @@ import { } from './ownershipScope'; import { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; -import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; +import type { + CoreCaveatBuilder, + ConvertEnumConfigToInputs, +} from '../coreCaveatBuilder'; // We want to allow the scope `type` to be passed as either an enum reference, -// or the enum's string value this generic accepts a union of scope configs, and -// converts them to an identical union except the `type` parameter is converted -// to a union of `ScopeType.XXXX | `${ScopeType.XXXX}`. -export type ConvertScopeConfigsToInputs = - T extends { type: ScopeType } - ? Omit & { type: T['type'] | `${T['type']}` } - : never; +// or the enum's string value. This uses the shared enum config conversion utility. +export type ConvertScopeConfigsToInputs = + ConvertEnumConfigToInputs; type ScopeConfigBase = | Erc20TransferScopeConfig From cd25cb80cfb7c85342fdb549e9ffdba2f02974ce Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Fri, 27 Feb 2026 22:45:56 +1300 Subject: [PATCH 11/18] refactor: import CaveatType in test files for improved type safety --- packages/delegator-e2e/test/caveats/caveatUtils.test.ts | 1 + packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts | 1 + packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts | 1 + packages/delegator-e2e/test/caveats/exactCalldata.test.ts | 1 + packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts | 1 + packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts | 1 + .../delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts | 1 + packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts | 1 + .../delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts | 1 + .../test/caveats/specificActionERC20TransferBatch.test.ts | 1 + 10 files changed, 10 insertions(+) diff --git a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts index f9182e49..fac1fa3a 100644 --- a/packages/delegator-e2e/test/caveats/caveatUtils.test.ts +++ b/packages/delegator-e2e/test/caveats/caveatUtils.test.ts @@ -3,6 +3,7 @@ import { encodeExecutionCalldatas, encodeDelegations, createCaveatBuilder, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createDelegation, diff --git a/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts b/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts index 23000a50..09d57727 100644 --- a/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts +++ b/packages/delegator-e2e/test/caveats/erc20PeriodTransfer.test.ts @@ -3,6 +3,7 @@ import { encodeExecutionCalldatas, encodeDelegations, createCaveatBuilder, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createExecution, diff --git a/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts b/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts index 9314cf78..6bda5a53 100644 --- a/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts +++ b/packages/delegator-e2e/test/caveats/erc20TransferAmount.test.ts @@ -4,6 +4,7 @@ import { encodeDelegations, createCaveatBuilder, hashDelegation, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createExecution, diff --git a/packages/delegator-e2e/test/caveats/exactCalldata.test.ts b/packages/delegator-e2e/test/caveats/exactCalldata.test.ts index 6deeb5a7..58ce8327 100644 --- a/packages/delegator-e2e/test/caveats/exactCalldata.test.ts +++ b/packages/delegator-e2e/test/caveats/exactCalldata.test.ts @@ -12,6 +12,7 @@ import { createCaveatBuilder, encodeDelegations, encodeExecutionCalldatas, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { gasPrice, diff --git a/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts b/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts index 8951ecc4..f66efccd 100644 --- a/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts +++ b/packages/delegator-e2e/test/caveats/multiTokenPeriod.test.ts @@ -3,6 +3,7 @@ import { encodeExecutionCalldatas, encodeDelegations, createCaveatBuilder, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createExecution, diff --git a/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts index 4e3eb8aa..24434ad6 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts @@ -4,6 +4,7 @@ import { encodeExecutionCalldatas, hashDelegation, createCaveatBuilder, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createExecution, diff --git a/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts index 9f38d1be..72b56059 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenPeriodTransfer.test.ts @@ -3,6 +3,7 @@ import { encodeExecutionCalldatas, encodeDelegations, createCaveatBuilder, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { Implementation, diff --git a/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts index 683f930f..72e02ee0 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenStreaming.test.ts @@ -3,6 +3,7 @@ import { encodeExecutionCalldatas, encodeDelegations, createCaveatBuilder, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createExecution, diff --git a/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts b/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts index f8739677..9110cfc6 100644 --- a/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts +++ b/packages/delegator-e2e/test/caveats/nativeTokenTransferAmount.test.ts @@ -4,6 +4,7 @@ import { encodeDelegations, createCaveatBuilder, hashDelegation, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { createExecution, diff --git a/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts b/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts index ae6a24e2..273be26e 100644 --- a/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts +++ b/packages/delegator-e2e/test/caveats/specificActionERC20TransferBatch.test.ts @@ -4,6 +4,7 @@ import { encodeDelegations, createCaveatBuilder, hashDelegation, + CaveatType, } from '@metamask/smart-accounts-kit/utils'; import { Implementation, From 6b915088dc72fe281e87570be462655bab3f5964 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Tue, 3 Mar 2026 15:55:28 +1300 Subject: [PATCH 12/18] refactor: enhance CaveatType integration to support both enum and string literals --- .../src/caveatBuilder/caveatBuilder.ts | 11 ++-- .../src/caveatBuilder/caveatType.ts | 8 +++ .../src/caveatBuilder/coreCaveatBuilder.ts | 4 +- .../src/caveatBuilder/index.ts | 2 +- .../src/caveatBuilder/resolveCaveats.ts | 6 ++- .../test/caveatBuilder/resolveCaveats.test.ts | 54 ++++++++++++++++++- 6 files changed, 75 insertions(+), 10 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts index e9eda865..2e9f732f 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts @@ -1,4 +1,5 @@ import type { Caveat, SmartAccountsEnvironment } from '../types'; +import type { CaveatTypeParam } from './caveatType'; type CaveatWithOptionalArgs = Omit & { args?: Caveat['args']; @@ -88,17 +89,21 @@ export class CaveatBuilder< /** * Adds a caveat using a named enforcer function. * - * @param name - The name of the enforcer function to use. + * Supports both enum and string literal forms: + * - caveatBuilder.addCaveat(CaveatType.AllowedMethods, { ... }) + * - caveatBuilder.addCaveat('allowedMethods', { ... }) + * + * @param name - The name of the enforcer function to use (enum or string). * @param config - The configuration to pass to the enforcer function. * @returns The CaveatBuilder instance for chaining. */ addCaveat( - name: TEnforcerName, + name: TEnforcerName | CaveatTypeParam, config: Parameters[1], ): CaveatBuilder; addCaveat( - nameOrCaveat: TEnforcerName | CaveatWithOptionalArgs, + nameOrCaveat: TEnforcerName | CaveatTypeParam | CaveatWithOptionalArgs, config?: Parameters[1], ): CaveatBuilder { if (typeof nameOrCaveat === 'object') { diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts index 027589df..27197115 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatType.ts @@ -36,3 +36,11 @@ 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}`; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index d1aa7347..57153136 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -130,8 +130,8 @@ type CoreCaveatMap = { */ export type CoreCaveatBuilder = CaveatBuilder; -// Re-export CaveatType for convenience -export { CaveatType } from './caveatType'; +// Re-export CaveatType and related types for convenience +export { CaveatType, type CaveatTypeParam } from './caveatType'; // Shared utility for allowing enum or string value for discriminated union types. // This converts a config type to accept both the enum value and its string representation. diff --git a/packages/smart-accounts-kit/src/caveatBuilder/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/index.ts index ea3fdf7c..3350d7db 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/index.ts @@ -1,7 +1,7 @@ // Core Caveat Builder export { createCaveatBuilder, CaveatType } from './coreCaveatBuilder'; -export type { CoreCaveatBuilder } from './coreCaveatBuilder'; +export type { CoreCaveatBuilder, CaveatTypeParam } from './coreCaveatBuilder'; // Caveat Builder implementation export type { CaveatBuilderConfig } from './caveatBuilder'; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts index 0c3c9af6..6fdc3393 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts @@ -1,4 +1,5 @@ import type { CaveatBuilder } from './caveatBuilder'; +import type { CaveatTypeParam } from './caveatType'; import type { CoreCaveatConfiguration } from './coreCaveatBuilder'; import { createCaveatBuilderFromScope, type ScopeConfig } from './scope'; import type { Caveat, SmartAccountsEnvironment } from '../types'; @@ -35,8 +36,9 @@ export const resolveCaveats = ({ try { if ('type' in caveat) { const { type, ...config } = caveat; - // Type assertion: addCaveat validates at runtime and throws if enforcer doesn't exist - (scopeCaveatBuilder.addCaveat as any)(type, config); + // Safe type cast: CaveatTypeParam accepts both enum and string forms + // addCaveat validates at runtime and throws if enforcer doesn't exist + scopeCaveatBuilder.addCaveat(type as CaveatTypeParam, config); } else { scopeCaveatBuilder.addCaveat(caveat); } diff --git a/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts index 5753b703..8b246076 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts @@ -74,7 +74,7 @@ describe('resolveCaveats', () => { expect(result.length).to.be.greaterThan(0); // Should have scope caveats }); - it('should handle CoreCaveatBuilder with named caveats', () => { + it('should handle CoreCaveatBuilder with named caveats using enum', () => { const caveatBuilder = createCaveatBuilder(environment, { allowInsecureUnrestrictedDelegation: true, }); @@ -91,6 +91,25 @@ describe('resolveCaveats', () => { expect(result).to.be.an('array'); expect(result.length).to.be.greaterThan(1); // Should have scope caveats + our added caveat }); + + it('should handle CoreCaveatBuilder with named caveats using string literal', () => { + const caveatBuilder = createCaveatBuilder(environment, { + allowInsecureUnrestrictedDelegation: true, + }); + // Use string literal instead of enum + caveatBuilder.addCaveat('allowedMethods', { + selectors: ['0x12345678'], + }); + + const result = resolveCaveats({ + environment, + scope: erc20Scope, + caveats: caveatBuilder as any, + }); + + expect(result).to.be.an('array'); + expect(result.length).to.be.greaterThan(1); // Should have scope caveats + our added caveat + }); }); describe('when caveats is an array', () => { @@ -111,7 +130,7 @@ describe('resolveCaveats', () => { expect(result).to.deep.include(mockCaveat2); }); - it('should resolve caveats from an array of CaveatConfiguration objects', () => { + it('should resolve caveats from an array of CaveatConfiguration objects with string types', () => { const caveatConfigs: CoreCaveatConfiguration[] = [ { type: 'allowedMethods', @@ -142,6 +161,37 @@ describe('resolveCaveats', () => { expect(result.length).to.be.greaterThan(scopeOnlyResult.length); }); + it('should resolve caveats from an array of CaveatConfiguration objects with enum types', () => { + const caveatConfigs: CoreCaveatConfiguration[] = [ + { + type: CaveatType.AllowedMethods, + selectors: ['0x12345678'], + }, + { + type: CaveatType.BlockNumber, + afterThreshold: 0n, + beforeThreshold: 1000n, + }, + ]; + + const result = resolveCaveats({ + environment, + scope: erc20Scope, + caveats: caveatConfigs, + }); + + expect(result).to.be.an('array'); + expect(result.length).to.be.greaterThan(2); // Should have scope caveats + our added caveats + + // Verify that the caveats were added by checking the result contains more than just scope caveats + const scopeOnlyResult = resolveCaveats({ + environment, + scope: erc20Scope, + caveats: [], + }); + expect(result.length).to.be.greaterThan(scopeOnlyResult.length); + }); + it('should resolve caveats from a mixed array of Caveat and CaveatConfiguration objects', () => { const mixedCaveats = [ mockCaveat1, From e4fe6acd5f23d575dfe2befcd86d73fe82deb1ad Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Tue, 3 Mar 2026 16:08:11 +1300 Subject: [PATCH 13/18] refactor: replace ConvertEnumConfigToInputs with ConvertCaveatConfigToInputs for improved clarity and consistency --- .../src/caveatBuilder/coreCaveatBuilder.ts | 22 +++++++------------ .../src/caveatBuilder/scope/index.ts | 13 ++++++----- 2 files changed, 15 insertions(+), 20 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 57153136..0740ca9d 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -133,19 +133,6 @@ export type CoreCaveatBuilder = CaveatBuilder; // Re-export CaveatType and related types for convenience export { CaveatType, type CaveatTypeParam } from './caveatType'; -// Shared utility for allowing enum or string value for discriminated union types. -// This converts a config type to accept both the enum value and its string representation. -// We preserve the discriminated union pattern by using TConfig['type'] instead of -// expanding to the full enum, which ensures TypeScript can narrow by the type field. -export type ConvertEnumConfigToInputs = - TConfig extends { type: string } - ? Omit & { type: TConfig['type'] | `${TConfig['type']}` } - : never; - -// Backward compatibility alias for caveat configs -export type ConvertCaveatConfigsToInputs = - ConvertEnumConfigToInputs; - type ExtractCaveatMapType> = TCaveatBuilder extends CaveatBuilder ? TCaveatMap : never; type ExtractedCoreMap = ExtractCaveatMapType; @@ -156,12 +143,19 @@ export type CaveatConfigurations = { } & Parameters[1]; }[keyof ExtractedCoreMap]; +// Convert a config type to accept both the enum value and its string representation, +// while preserving the discriminated union pattern. +type ConvertCaveatConfigToInputs = + TConfig extends { type: string } + ? Omit & { type: TConfig['type'] | `${TConfig['type']}` } + : never; + export type CaveatConfiguration< TCaveatBuilder extends CaveatBuilder, CaveatMap = ExtractCaveatMapType, > = CaveatMap extends Record any> - ? ConvertEnumConfigToInputs< + ? ConvertCaveatConfigToInputs< { [TType in keyof CaveatMap]: { type: TType extends string ? TType : never; diff --git a/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts b/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts index a0155dc9..43619876 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts @@ -36,15 +36,16 @@ import { } from './ownershipScope'; import { ScopeType } from '../../constants'; import type { SmartAccountsEnvironment } from '../../types'; -import type { - CoreCaveatBuilder, - ConvertEnumConfigToInputs, -} from '../coreCaveatBuilder'; +import type { CoreCaveatBuilder } from '../coreCaveatBuilder'; // We want to allow the scope `type` to be passed as either an enum reference, -// or the enum's string value. This uses the shared enum config conversion utility. +// or the enum's string value this generic accepts a union of scope configs, and +// converts them to an identical union except the `type` parameter is converted +// to a union of `ScopeType.XXXX | `${ScopeType.XXXX}`. export type ConvertScopeConfigsToInputs = - ConvertEnumConfigToInputs; + TConfig extends { type: ScopeType } + ? Omit & { type: TConfig['type'] | `${TConfig['type']}` } + : never; type ScopeConfigBase = | Erc20TransferScopeConfig From 3121c9131d0dce03e25665f08c21b915a1d33832 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 4 Mar 2026 11:25:16 +1300 Subject: [PATCH 14/18] refactor: update addCaveat to accept string literals for improved flexibility --- .../test/caveatBuilder/caveatTypeEnum.test.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts index 982b94ef..87201104 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts @@ -140,7 +140,7 @@ describe('CaveatType enum usage patterns', () => { .build(); const stringResult = builderWithString - .addCaveat(CaveatType.AllowedMethods, { selectors }) + .addCaveat('allowedMethods', { selectors }) .build(); // Both should produce the same result @@ -408,12 +408,12 @@ describe('CaveatType enum usage patterns', () => { it('should allow string literals in addCaveat', () => { const builder = createCaveatBuilder(environment); - // This should also compile without errors - builder.addCaveat(CaveatType.AllowedMethods, { + // String literals (backward compatibility): addCaveat accepts CaveatTypeParam + builder.addCaveat('allowedMethods', { selectors: ['0x12345678'], }); - builder.addCaveat(CaveatType.ValueLte, { maxValue: 1000n }); - builder.addCaveat(CaveatType.AllowedTargets, { + builder.addCaveat('valueLte', { maxValue: 1000n }); + builder.addCaveat('allowedTargets', { targets: [randomAddress()], }); From c54863a582e136d27112d5844442bc7b02023800 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 4 Mar 2026 14:14:59 +1300 Subject: [PATCH 15/18] refactor: add tests for string literal caveat names in createCaveatBuilder --- .../caveatBuilder/createCaveatBuilder.test.ts | 330 ++++++++++++++++++ 1 file changed, 330 insertions(+) diff --git a/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts index bed5b36b..26a683ba 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/createCaveatBuilder.test.ts @@ -501,4 +501,334 @@ describe('createCaveatBuilder()', () => { expect(isAddress(caveat.enforcer)).to.equal(true); }); + + describe('string literal caveat names', () => { + it("should add an 'allowedMethods' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const selectors = [randomBytes(4), randomBytes(4)]; + + const caveats = builder + .addCaveat('allowedMethods', { selectors }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.AllowedMethodsEnforcer, + terms: concat(selectors), + args: '0x00', + }, + ]); + }); + + it("should add an 'allowedTargets' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const targets: [Address, Address] = [randomAddress(), randomAddress()]; + + const caveats = builder.addCaveat('allowedTargets', { targets }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.AllowedTargetsEnforcer, + terms: targets[0] + targets[1]?.slice(2), + args: '0x00', + }, + ]); + }); + + it("should add a 'deployed' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const contractAddress = randomAddress(); + const salt = randomBytes(32); + const bytecode = randomBytes(256); + + const caveats = builder + .addCaveat('deployed', { contractAddress, salt, bytecode }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.DeployedEnforcer, + terms: concat([contractAddress, pad(salt, { size: 32 }), bytecode]), + args: '0x00', + }, + ]); + }); + + it("should add an 'allowedCalldata' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const value = randomBytes(128); + const startIndex = Math.floor(Math.random() * 2 ** 32); + + const caveats = builder + .addCaveat('allowedCalldata', { startIndex, value }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.AllowedCalldataEnforcer, + terms: concat([toHex(startIndex, { size: 32 }), value]), + args: '0x00', + }, + ]); + }); + + it("should add an 'erc20BalanceChange' caveat using string literal", () => { + 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('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', + }, + ]); + }); + + it("should add a 'valueLte' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const maxValue = BigInt( + Math.floor(Math.random() * Number.MAX_SAFE_INTEGER), + ); + + const caveats = builder.addCaveat('valueLte', { maxValue }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ValueLteEnforcer, + terms: toHex(maxValue, { size: 32 }), + args: '0x00', + }, + ]); + }); + + it("should add a 'limitedCalls' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const limit = Math.floor(Math.random() * Number.MAX_SAFE_INTEGER); + const caveats = builder.addCaveat('limitedCalls', { limit }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.LimitedCallsEnforcer, + terms: toHex(limit, { size: 32 }), + args: '0x00', + }, + ]); + }); + + it("should add an 'id' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const idValue = BigInt(Math.floor(Math.random() * 2 ** 32)); + const caveats = builder.addCaveat('id', { id: idValue }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.IdEnforcer, + terms: toHex(idValue, { size: 32 }), + args: '0x00', + }, + ]); + }); + + it("should add a 'nonce' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const nonce = randomBytes(16); + const caveats = builder.addCaveat('nonce', { nonce }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.NonceEnforcer, + terms: pad(nonce, { size: 32 }), + args: '0x00', + }, + ]); + }); + + it("should add a 'timestamp' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const afterThreshold = 1000; + const beforeThreshold = 2000; + + const caveats = builder + .addCaveat('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', + }, + ]); + }); + + it("should add a 'blockNumber' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const afterThreshold = 1000n; + const beforeThreshold = 2000n; + + const caveats = builder + .addCaveat('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', + }, + ]); + }); + + it("should add a 'nativeTokenTransferAmount' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + const maxAmount = 1000000000000000000n; // 1 ETH in wei + + const caveats = builder + .addCaveat('nativeTokenTransferAmount', { maxAmount }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: + environment.caveatEnforcers.NativeTokenTransferAmountEnforcer, + terms: toHex(maxAmount, { size: 32 }), + args: '0x00', + }, + ]); + }); + + it("should add a 'nativeBalanceChange' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + const recipient = randomAddress(); + const minBalance = 500000000000000000n; // 0.5 ETH in wei + + const caveats = builder + .addCaveat('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', + }, + ]); + }); + + it("should add a 'nativeTokenPayment' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + const amount = 1000000000000000000n; // 1 ETH in wei + const recipient = randomAddress('lowercase'); + + const caveats = builder + .addCaveat('nativeTokenPayment', { recipient, amount }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.NativeTokenPaymentEnforcer, + terms: concat([recipient, toHex(amount, { size: 32 })]), + args: '0x00', + }, + ]); + }); + + it("should add an 'erc20TransferAmount' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + + const tokenAddress = randomAddress(); + const maxAmount = 2000n; + + const caveats = builder + .addCaveat('erc20TransferAmount', { tokenAddress, maxAmount }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ERC20TransferAmountEnforcer, + terms: concat([tokenAddress, toHex(maxAmount, { size: 32 })]), + args: '0x00', + }, + ]); + }); + + it("should add a 'redeemer' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + const redeemerAddress = randomAddress(); + + const caveats = builder + .addCaveat('redeemer', { redeemers: [redeemerAddress] }) + .build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.RedeemerEnforcer, + terms: redeemerAddress, + args: '0x00', + }, + ]); + }); + + it("should add an 'argsEqualityCheck' caveat using string literal", () => { + const builder = createCaveatBuilder(environment); + const args = '0x1234567890'; + + const caveats = builder.addCaveat('argsEqualityCheck', { args }).build(); + + expect(caveats).to.deep.equal([ + { + enforcer: environment.caveatEnforcers.ArgsEqualityCheckEnforcer, + terms: args, + args: '0x00', + }, + ]); + }); + }); }); From 8c8b7664e9c10dd4420b274e953492a56706ab79 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 4 Mar 2026 14:56:04 +1300 Subject: [PATCH 16/18] refactor: add string literal test cases for AllowedCalldata caveat handling --- .../test/caveats/allowedCalldata.test.ts | 71 ++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts b/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts index 9cba467f..c49715f3 100644 --- a/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts +++ b/packages/delegator-e2e/test/caveats/allowedCalldata.test.ts @@ -78,6 +78,13 @@ test('maincase: Bob redeems the delegation with the exact calldata', async () => await runTest_expectSuccess(newCount, [{ from: 4, calldata }]); }); +test('maincase (string literal): Bob redeems the delegation with the exact calldata', async () => { + const calldata = randomBytes(32); + const newCount = hexToBigInt(calldata); + + await runTest_expectSuccess(newCount, [{ from: 4, calldata }], 'allowedCalldata'); +}); + test('Bob redeems the delegation where the delegation requires a substring of the calldata', async () => { const calldata = randomBytes(32); const newCount = hexToBigInt(calldata); @@ -89,6 +96,19 @@ test('Bob redeems the delegation where the delegation requires a substring of th ]); }); +test('Bob redeems the delegation where the delegation requires a substring of the calldata (string literal)', async () => { + const calldata = randomBytes(32); + const newCount = hexToBigInt(calldata); + + const requiredCalldata = slice(calldata, 0, 34); + + await runTest_expectSuccess( + newCount, + [{ from: 4, calldata: requiredCalldata }], + 'allowedCalldata', + ); +}); + test('Bob redeems the delegation where the calldata matches multiple caveats', async () => { const calldata = randomBytes(32); const newCount = hexToBigInt(calldata); @@ -102,6 +122,23 @@ test('Bob redeems the delegation where the calldata matches multiple caveats', a ]); }); +test('Bob redeems the delegation where the calldata matches multiple caveats (string literal)', async () => { + const calldata = randomBytes(32); + const newCount = hexToBigInt(calldata); + + const firstSlice = slice(calldata, 0, 34); + const secondSlice = slice(calldata, 20); + + await runTest_expectSuccess( + newCount, + [ + { from: 4, calldata: firstSlice }, + { from: 24, calldata: secondSlice }, + ], + 'allowedCalldata', + ); +}); + test('Bob attempts to redeem the delegation with incorrect calldata', async () => { const newCount = 1n; @@ -118,6 +155,23 @@ test('Bob attempts to redeem the delegation with incorrect calldata', async () = ); }); +test('Bob attempts to redeem the delegation with incorrect calldata (string literal)', async () => { + const newCount = 1n; + + const executedCalldata = encodeFunctionData({ + abi: aliceCounter.abi, + functionName: 'setCount', + args: [newCount], + }); + + await runTest_expectFailure( + executedCalldata, + [{ from: 0, calldata: randomBytes(32) }], + 'AllowedCalldataEnforcer:invalid-calldata', + 'allowedCalldata', + ); +}); + test('Bob attempts to redeem the delegation with no calldata', async () => { const executedCalldata = '0x'; @@ -128,9 +182,21 @@ test('Bob attempts to redeem the delegation with no calldata', async () => { ); }); +test('Bob attempts to redeem the delegation with no calldata (string literal)', async () => { + const executedCalldata = '0x'; + + await runTest_expectFailure( + executedCalldata, + [{ from: 0, calldata: randomBytes(32) }], + 'AllowedCalldataEnforcer:invalid-calldata', + 'allowedCalldata', + ); +}); + const runTest_expectSuccess = async ( newCount: bigint, caveats: { from: number; calldata: Hex }[], + caveatForm: typeof CaveatType.AllowedCalldata | 'allowedCalldata' = CaveatType.AllowedCalldata, ) => { const { environment } = aliceSmartAccount; @@ -141,7 +207,7 @@ const runTest_expectSuccess = async ( salt: '0x0', caveats: caveats .reduce((builder, caveat) => { - builder.addCaveat(CaveatType.AllowedCalldata, { + builder.addCaveat(caveatForm, { startIndex: caveat.from, value: caveat.calldata, }); @@ -209,6 +275,7 @@ const runTest_expectFailure = async ( executedCalldata: Hex, caveats: { from: number; calldata: Hex }[], expectedError: string, + caveatForm: typeof CaveatType.AllowedCalldata | 'allowedCalldata' = CaveatType.AllowedCalldata, ) => { const { environment } = aliceSmartAccount; @@ -219,7 +286,7 @@ const runTest_expectFailure = async ( salt: '0x0', caveats: caveats .reduce((builder, caveat) => { - builder.addCaveat(CaveatType.AllowedCalldata, { + builder.addCaveat(caveatForm, { startIndex: caveat.from, value: caveat.calldata, }); From 9296e17aa835d51e5beeab3699a9112cfc28ce63 Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 4 Mar 2026 15:22:47 +1300 Subject: [PATCH 17/18] refactor: update addCaveat method to enforce registered enforcer names and support CaveatTypeParam --- .../src/caveatBuilder/caveatBuilder.ts | 10 +++++----- .../src/caveatBuilder/coreCaveatBuilder.ts | 18 ++++++++++++++++-- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts index 2e9f732f..0bfb0f44 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/caveatBuilder.ts @@ -1,5 +1,4 @@ import type { Caveat, SmartAccountsEnvironment } from '../types'; -import type { CaveatTypeParam } from './caveatType'; type CaveatWithOptionalArgs = Omit & { args?: Caveat['args']; @@ -89,21 +88,22 @@ export class CaveatBuilder< /** * Adds a caveat using a named enforcer function. * - * Supports both enum and string literal forms: + * Only accepts caveat types that are registered on this builder (keys of TCaveatBuilderMap). + * Supports both enum and string literal when the builder includes that enforcer, e.g.: * - caveatBuilder.addCaveat(CaveatType.AllowedMethods, { ... }) * - caveatBuilder.addCaveat('allowedMethods', { ... }) * - * @param name - The name of the enforcer function to use (enum or string). + * @param name - The name of the enforcer function to use (must be registered on this builder). * @param config - The configuration to pass to the enforcer function. * @returns The CaveatBuilder instance for chaining. */ addCaveat( - name: TEnforcerName | CaveatTypeParam, + name: TEnforcerName, config: Parameters[1], ): CaveatBuilder; addCaveat( - nameOrCaveat: TEnforcerName | CaveatTypeParam | CaveatWithOptionalArgs, + nameOrCaveat: TEnforcerName | CaveatWithOptionalArgs, config?: Parameters[1], ): CaveatBuilder { if (typeof nameOrCaveat === 'object') { diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 0740ca9d..639a524e 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -12,7 +12,7 @@ import { import { blockNumber, blockNumberBuilder } from './blockNumberBuilder'; import type { CaveatBuilderConfig } from './caveatBuilder'; import { CaveatBuilder } from './caveatBuilder'; -import type { CaveatType } from './caveatType'; +import type { CaveatType, CaveatTypeParam } from './caveatType'; import { deployed, deployedBuilder } from './deployedBuilder'; import { erc1155BalanceChange, @@ -127,8 +127,22 @@ type CoreCaveatMap = { * A caveat builder type that includes all core caveat types pre-configured. * This type represents a fully configured caveat builder with all the standard * caveat builders available for use. + * + * The addCaveat(name, config) overload is widened to accept CaveatTypeParam + * (enum or string literal) so that both CaveatType.AllowedMethods and + * 'allowedMethods' are accepted. Extended builders keep strict keyof Map typing. */ -export type CoreCaveatBuilder = CaveatBuilder; +/** Union of all core caveat config types (for the CaveatTypeParam-accepting overload). */ +type CoreCaveatConfigUnion = { + [K in keyof CoreCaveatMap]: Parameters[1]; +}[keyof CoreCaveatMap]; + +export type CoreCaveatBuilder = CaveatBuilder & { + addCaveat( + name: CaveatTypeParam, + config: CoreCaveatConfigUnion, + ): CoreCaveatBuilder; +}; // Re-export CaveatType and related types for convenience export { CaveatType, type CaveatTypeParam } from './caveatType'; From 0c71b080f3d78c54f29afa18fb64b74715a5fdce Mon Sep 17 00:00:00 2001 From: MJ Kiwi Date: Wed, 4 Mar 2026 15:35:31 +1300 Subject: [PATCH 18/18] refactor: enhance type safety for addCaveat method with string literal support and update tests for compile-time validation --- .../src/caveatBuilder/coreCaveatBuilder.ts | 21 +++++++++++++------ .../test/caveatBuilder/caveatTypeEnum.test.ts | 10 +++++++++ 2 files changed, 25 insertions(+), 6 deletions(-) diff --git a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts index 639a524e..d73136ad 100644 --- a/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts +++ b/packages/smart-accounts-kit/src/caveatBuilder/coreCaveatBuilder.ts @@ -132,15 +132,24 @@ type CoreCaveatMap = { * (enum or string literal) so that both CaveatType.AllowedMethods and * 'allowedMethods' are accepted. Extended builders keep strict keyof Map typing. */ -/** Union of all core caveat config types (for the CaveatTypeParam-accepting overload). */ -type CoreCaveatConfigUnion = { +/** + * Maps each caveat type (enum and string literal) to its config type. + * Ensures addCaveat(name, config) is type-checked per caveat even when + * using string literals (e.g. 'allowedMethods'), preserving backward + * compatibility and preventing wrong-config-at-runtime regressions. + */ +type CoreCaveatParamToConfig = { [K in keyof CoreCaveatMap]: Parameters[1]; -}[keyof CoreCaveatMap]; +} & { + [K in CaveatType as `${K}`]: K extends keyof CoreCaveatMap + ? Parameters[1] + : never; +}; export type CoreCaveatBuilder = CaveatBuilder & { - addCaveat( - name: CaveatTypeParam, - config: CoreCaveatConfigUnion, + addCaveat( + name: TKey, + config: CoreCaveatParamToConfig[TKey], ): CoreCaveatBuilder; }; diff --git a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts index 87201104..3ab0c170 100644 --- a/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts +++ b/packages/smart-accounts-kit/test/caveatBuilder/caveatTypeEnum.test.ts @@ -421,6 +421,16 @@ describe('CaveatType enum usage patterns', () => { expect(caveats).to.have.lengthOf(3); }); + it('should reject wrong config for string literal caveat type at compile time', () => { + const builder = createCaveatBuilder(environment); + + // Type-only assertion: wrong config must be a compile error (never executed). + if (false as boolean) { + // @ts-expect-error - wrong config for 'allowedMethods' (expects selectors, not maxValue) + builder.addCaveat('allowedMethods', { maxValue: 1000n }); + } + }); + it('should allow CaveatType enum in caveats array', () => { const scope = { type: ScopeType.Erc20TransferAmount as const,