Skip to content
Merged
4 changes: 4 additions & 0 deletions packages/smart-accounts-kit/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Allow scope type to be specified either as `ScopeType` enum, or string literal ([#133](https://github.com/MetaMask/smart-accounts-kit/pull/133))

## [0.4.0-beta.1]

### Added
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type {
erc20PeriodTransfer,
Erc20PeriodTransferBuilderConfig,
} from '../erc20PeriodTransferBuilder';
import type { Erc20PeriodTransferBuilderConfig } from '../erc20PeriodTransferBuilder';

export type Erc20PeriodicScopeConfig = {
type: typeof erc20PeriodTransfer;
type: ScopeType.Erc20PeriodTransfer;
} & Erc20PeriodTransferBuilderConfig;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type {
erc20Streaming,
Erc20StreamingBuilderConfig,
} from '../erc20StreamingBuilder';
import type { Erc20StreamingBuilderConfig } from '../erc20StreamingBuilder';

export type Erc20StreamingScopeConfig = {
type: typeof erc20Streaming;
type: ScopeType.Erc20Streaming;
} & Erc20StreamingBuilderConfig;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type {
erc20TransferAmount,
Erc20TransferAmountBuilderConfig,
} from '../erc20TransferAmountBuilder';
import type { Erc20TransferAmountBuilderConfig } from '../erc20TransferAmountBuilder';

export type Erc20TransferScopeConfig = {
type: typeof erc20TransferAmount;
type: ScopeType.Erc20TransferAmount;
} & Erc20TransferAmountBuilderConfig;

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import { hasProperties } from '../../utils';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type {
erc721Transfer,
Erc721TransferBuilderConfig,
} from '../erc721TransferBuilder';
import type { Erc721TransferBuilderConfig } from '../erc721TransferBuilder';

export type Erc721ScopeBaseConfig = {
type: typeof erc721Transfer;
type: ScopeType.Erc721Transfer;
};

export type Erc721ScopeConfig = Erc721ScopeBaseConfig &
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import { hasProperties } from '../../utils';
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
Expand All @@ -9,7 +10,7 @@ import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
import type { ValueLteBuilderConfig } from '../valueLteBuilder';

type FunctionCallScopeBaseConfig = {
type: 'functionCall';
type: ScopeType.FunctionCall;
allowedCalldata?: AllowedCalldataBuilderConfig[];
exactCalldata?: ExactCalldataBuilderConfig;
valueLte?: ValueLteBuilderConfig;
Expand Down
96 changes: 65 additions & 31 deletions packages/smart-accounts-kit/src/caveatBuilder/scope/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,18 +34,20 @@ import {
createOwnershipCaveatBuilder,
type OwnershipScopeConfig,
} from './ownershipScope';
import { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
// Import caveat builder name constants
import { erc20PeriodTransfer } from '../erc20PeriodTransferBuilder';
import { erc20Streaming } from '../erc20StreamingBuilder';
import { erc20TransferAmount } from '../erc20TransferAmountBuilder';
import { erc721Transfer } from '../erc721TransferBuilder';
import { nativeTokenPeriodTransfer } from '../nativeTokenPeriodTransferBuilder';
import { nativeTokenStreaming } from '../nativeTokenStreamingBuilder';
import { nativeTokenTransferAmount } from '../nativeTokenTransferAmountBuilder';
import { ownershipTransfer } from '../ownershipTransferBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';

export type ScopeConfig =
// 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 }> =
T extends { type: ScopeType }
? Omit<T, 'type'> & { type: T['type'] | `${T['type']}` }
: never;

type ScopeConfigBase =
| Erc20TransferScopeConfig
| Erc20StreamingScopeConfig
| Erc20PeriodicScopeConfig
Expand All @@ -56,32 +58,64 @@ export type ScopeConfig =
| OwnershipScopeConfig
| FunctionCallScopeConfig;

export type ScopeConfig = ConvertScopeConfigsToInputs<ScopeConfigBase>;

const normalizeScopeConfig = (config: ScopeConfig): ScopeConfigBase => {
return {
...config,
type: config.type as ScopeType,
} as ScopeConfigBase;
};

export const createCaveatBuilderFromScope = (
environment: SmartAccountsEnvironment,
scopeConfig: ScopeConfig,
) => {
switch (scopeConfig.type) {
case erc20TransferAmount:
return createErc20TransferCaveatBuilder(environment, scopeConfig);
case erc20Streaming:
return createErc20StreamingCaveatBuilder(environment, scopeConfig);
case erc20PeriodTransfer:
return createErc20PeriodicCaveatBuilder(environment, scopeConfig);
case nativeTokenTransferAmount:
return createNativeTokenTransferCaveatBuilder(environment, scopeConfig);
case nativeTokenStreaming:
return createNativeTokenStreamingCaveatBuilder(environment, scopeConfig);
case nativeTokenPeriodTransfer:
return createNativeTokenPeriodicCaveatBuilder(environment, scopeConfig);
case erc721Transfer:
return createErc721CaveatBuilder(environment, scopeConfig);
case ownershipTransfer:
return createOwnershipCaveatBuilder(environment, scopeConfig);
case 'functionCall':
return createFunctionCallCaveatBuilder(environment, scopeConfig);
): CoreCaveatBuilder => {
const normalizedScopeConfig = normalizeScopeConfig(scopeConfig);

switch (normalizedScopeConfig.type) {
case ScopeType.Erc20TransferAmount:
return createErc20TransferCaveatBuilder(
environment,
normalizedScopeConfig,
);
case ScopeType.Erc20Streaming:
return createErc20StreamingCaveatBuilder(
environment,
normalizedScopeConfig,
);
case ScopeType.Erc20PeriodTransfer:
return createErc20PeriodicCaveatBuilder(
environment,
normalizedScopeConfig,
);
case ScopeType.NativeTokenTransferAmount:
return createNativeTokenTransferCaveatBuilder(
environment,
normalizedScopeConfig,
);
case ScopeType.NativeTokenStreaming:
return createNativeTokenStreamingCaveatBuilder(
environment,
normalizedScopeConfig,
);
case ScopeType.NativeTokenPeriodTransfer:
return createNativeTokenPeriodicCaveatBuilder(
environment,
normalizedScopeConfig,
);
case ScopeType.Erc721Transfer:
return createErc721CaveatBuilder(environment, normalizedScopeConfig);
case ScopeType.OwnershipTransfer:
return createOwnershipCaveatBuilder(environment, normalizedScopeConfig);
case ScopeType.FunctionCall:
return createFunctionCallCaveatBuilder(
environment,
normalizedScopeConfig,
);
default:
// eslint-disable-next-line no-case-declarations
const exhaustivenessCheck: never = scopeConfig;
const exhaustivenessCheck: never = normalizedScopeConfig;
throw new Error(
`Invalid scope type: ${(exhaustivenessCheck as { type: string }).type}`,
);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
import type {
nativeTokenPeriodTransfer,
NativeTokenPeriodTransferBuilderConfig,
} from '../nativeTokenPeriodTransferBuilder';
import type { NativeTokenPeriodTransferBuilderConfig } from '../nativeTokenPeriodTransferBuilder';

export type NativeTokenPeriodicScopeConfig = {
type: typeof nativeTokenPeriodTransfer;
type: ScopeType.NativeTokenPeriodTransfer;
allowedCalldata?: AllowedCalldataBuilderConfig[];
exactCalldata?: ExactCalldataBuilderConfig;
} & NativeTokenPeriodTransferBuilderConfig;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
import type {
nativeTokenStreaming,
NativeTokenStreamingBuilderConfig,
} from '../nativeTokenStreamingBuilder';
import type { NativeTokenStreamingBuilderConfig } from '../nativeTokenStreamingBuilder';

export type NativeTokenStreamingScopeConfig = {
type: typeof nativeTokenStreaming;
type: ScopeType.NativeTokenStreaming;
allowedCalldata?: AllowedCalldataBuilderConfig[];
exactCalldata?: ExactCalldataBuilderConfig;
} & NativeTokenStreamingBuilderConfig;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
import type {
nativeTokenTransferAmount,
NativeTokenTransferAmountBuilderConfig,
} from '../nativeTokenTransferAmountBuilder';
import type { NativeTokenTransferAmountBuilderConfig } from '../nativeTokenTransferAmountBuilder';

export type NativeTokenTransferScopeConfig = {
type: typeof nativeTokenTransferAmount;
type: ScopeType.NativeTokenTransferAmount;
allowedCalldata?: AllowedCalldataBuilderConfig[];
exactCalldata?: ExactCalldataBuilderConfig;
} & NativeTokenTransferAmountBuilderConfig;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
import type { ScopeType } from '../../constants';
import type { SmartAccountsEnvironment } from '../../types';
import { hasProperties } from '../../utils';
import { createCaveatBuilder } from '../coreCaveatBuilder';
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
import type {
ownershipTransfer,
OwnershipTransferBuilderConfig,
} from '../ownershipTransferBuilder';
import type { OwnershipTransferBuilderConfig } from '../ownershipTransferBuilder';

type OwnershipScopeBaseConfig = {
type: typeof ownershipTransfer;
type: ScopeType.OwnershipTransfer;
};

export type OwnershipScopeConfig = OwnershipScopeBaseConfig &
Expand Down
16 changes: 16 additions & 0 deletions packages/smart-accounts-kit/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,19 @@ export enum TransferWindow {
Quarterly = 7776000, // 60 * 60 * 24 * 90 (seconds)
Yearly = 31536000, // 60 * 60 * 24 * 365 (seconds)
}

/**
* Scope types for configuring delegation permissions.
* Used with createCaveatBuilderFromScope to specify the type of permission scope.
*/
export enum ScopeType {
Erc20TransferAmount = 'erc20TransferAmount',
Erc20Streaming = 'erc20Streaming',
Erc20PeriodTransfer = 'erc20PeriodTransfer',
NativeTokenTransferAmount = 'nativeTokenTransferAmount',
NativeTokenStreaming = 'nativeTokenStreaming',
NativeTokenPeriodTransfer = 'nativeTokenPeriodTransfer',
Erc721Transfer = 'erc721Transfer',
OwnershipTransfer = 'ownershipTransfer',
FunctionCall = 'functionCall',
}
2 changes: 1 addition & 1 deletion packages/smart-accounts-kit/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ export {
getSmartAccountsEnvironment,
} from './smartAccountsEnvironment';

export { Implementation, TransferWindow } from './constants';
export { Implementation, TransferWindow, ScopeType } from './constants';

export { createExecution, ExecutionMode } from './executions';

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { privateKeyToAccount } from 'viem/accounts';
import { describe, expect, it } from 'vitest';

import { ScopeType } from '../../../src/constants';
import { createDelegation } from '../../../src/delegation';
import * as DelegationManager from '../../../src/DelegationFramework/DelegationManager';
import { ExecutionMode, createExecution } from '../../../src/executions';
Expand Down Expand Up @@ -95,7 +96,7 @@ describe('DelegationManager - Delegation Management', () => {
from: alice.address,
environment,
scope: {
type: 'functionCall',
type: ScopeType.FunctionCall,
targets: [alice.address],
selectors: ['0x00000000'],
},
Expand All @@ -118,7 +119,7 @@ describe('DelegationManager - Delegation Management', () => {
from: alice.address,
environment,
scope: {
type: 'functionCall',
type: ScopeType.FunctionCall,
targets: [alice.address],
selectors: ['0x00000000'],
},
Expand All @@ -141,7 +142,7 @@ describe('DelegationManager - Delegation Management', () => {
from: alice.address,
environment,
scope: {
type: 'functionCall',
type: ScopeType.FunctionCall,
targets: [alice.address],
selectors: ['0x00000000'],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { CoreCaveatConfiguration } from '../../src/caveatBuilder/coreCaveat
import { createCaveatBuilder } from '../../src/caveatBuilder/coreCaveatBuilder';
import { resolveCaveats } from '../../src/caveatBuilder/resolveCaveats';
import type { ScopeConfig } from '../../src/caveatBuilder/scope';
import { ScopeType } from '../../src/constants';
import type { Caveat, SmartAccountsEnvironment } from '../../src/types';
import { randomAddress } from '../utils';

Expand Down Expand Up @@ -32,7 +33,7 @@ describe('resolveCaveats', () => {
};

const erc20Scope: ScopeConfig = {
type: 'erc20TransferAmount',
type: ScopeType.Erc20TransferAmount,
tokenAddress: randomAddress(),
maxAmount: 1000n,
};
Expand Down Expand Up @@ -182,7 +183,7 @@ describe('resolveCaveats', () => {
describe('scope', () => {
it('should work with different scope types', () => {
const erc721Scope: ScopeConfig = {
type: 'erc721Transfer',
type: ScopeType.Erc721Transfer,
tokenAddress: randomAddress(),
tokenId: 123n,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { describe, it, expect } from 'vitest';

import { createErc20PeriodicCaveatBuilder } from '../../../src/caveatBuilder/scope/erc20PeriodicScope';
import type { Erc20PeriodicScopeConfig } from '../../../src/caveatBuilder/scope/erc20PeriodicScope';
import { ScopeType } from '../../../src/constants';
import type { SmartAccountsEnvironment } from '../../../src/types';
import { randomAddress } from '../../utils';

Expand All @@ -16,7 +17,7 @@ describe('createErc20PeriodicCaveatBuilder', () => {

it('creates an ERC20 periodic transfer CaveatBuilder', () => {
const config: Erc20PeriodicScopeConfig = {
type: 'erc20PeriodTransfer',
type: ScopeType.Erc20PeriodTransfer,
tokenAddress: randomAddress(),
periodAmount: 1000n,
periodDuration: 1000,
Expand Down
Loading
Loading