Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 27 additions & 1 deletion packages/delegation-core/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,33 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Uncategorized
### Added

- Add terms builders for all enforcers implemented in @metamask/smart-accounts-kit ([#139](https://github.com/metamask/smart-accounts-kit/pull/139))
- `createAllowedMethodsTerms`
- `createAllowedTargetsTerms`
- `createArgsEqualityCheckTerms`
- `createBlockNumberTerms`
- `createDeployedTerms`
- `createERC1155BalanceChangeTerms`
- `createERC20BalanceChangeTerms`
- `createERC20TransferAmountTerms`
- `createERC721BalanceChangeTerms`
- `createERC721TransferTerms`
- `createExactCalldataBatchTerms`
- `createExactExecutionTerms`
- `createExactExecutionBatchTerms`
- `createIdTerms`
- `createLimitedCallsTerms`
- `createMultiTokenPeriodTerms`
- `createNativeBalanceChangeTerms`
- `createNativeTokenPaymentTerms`
- `createNativeTokenTransferAmountTerms`
- `createOwnershipTransferTerms`
- `createRedeemerTerms`
- `createSpecificActionERC20TransferBatchTerms`

### Fixed

- Resolve yarn peer dependency warnings ([#123](https://github.com/metamask/smart-accounts-kit/pull/123))

Expand Down
2 changes: 1 addition & 1 deletion packages/delegation-core/src/caveats/allowedCalldata.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import { bytesToHex, remove0x, type BytesLike } from '@metamask/utils';

import { toHexString } from '../internalUtils';
import {
defaultOptions,
prepareResult,
type EncodingOptions,
type ResultValue,
} from '../returns';
import type { Hex } from '../types';
import { toHexString } from '../utils';

/**
* Terms for configuring an AllowedCalldata caveat.
Expand Down
78 changes: 78 additions & 0 deletions packages/delegation-core/src/caveats/allowedMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
import { bytesToHex, isHexString, type BytesLike } from '@metamask/utils';

import { concatHex } from '../internalUtils';
import {
defaultOptions,
prepareResult,
type EncodingOptions,
type ResultValue,
} from '../returns';
import type { Hex } from '../types';

/**
* Terms for configuring an AllowedMethods caveat.
*/
export type AllowedMethodsTerms = {
/** An array of 4-byte method selectors that the delegate is allowed to call. */
selectors: BytesLike[];
};

const FUNCTION_SELECTOR_STRING_LENGTH = 10; // 0x + 8 hex chars
const INVALID_SELECTOR_ERROR =
'Invalid selector: must be a 4 byte hex string, abi function signature, or AbiFunction';

/**
* Creates terms for an AllowedMethods caveat that restricts calls to a set of method selectors.
*
* @param terms - The terms for the AllowedMethods caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as concatenated method selectors.
* @throws Error if the selectors array is empty or contains invalid selectors.
*/
export function createAllowedMethodsTerms(
terms: AllowedMethodsTerms,
encodingOptions?: EncodingOptions<'hex'>,
): Hex;
export function createAllowedMethodsTerms(
terms: AllowedMethodsTerms,
encodingOptions: EncodingOptions<'bytes'>,
): Uint8Array;
/**
* Creates terms for an AllowedMethods caveat that restricts calls to a set of method selectors.
*
* @param terms - The terms for the AllowedMethods caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as concatenated method selectors.
* @throws Error if the selectors array is empty or contains invalid selectors.
*/
export function createAllowedMethodsTerms(
terms: AllowedMethodsTerms,
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
): Hex | Uint8Array {
const { selectors } = terms;

if (!selectors || selectors.length === 0) {
throw new Error('Invalid selectors: must provide at least one selector');
}

const normalizedSelectors = selectors.map((selector) => {
if (typeof selector === 'string') {
if (
isHexString(selector) &&
selector.length === FUNCTION_SELECTOR_STRING_LENGTH
) {
return selector;
}
throw new Error(INVALID_SELECTOR_ERROR);
}

if (selector.length !== 4) {
throw new Error(INVALID_SELECTOR_ERROR);
}

return bytesToHex(selector);
});

const hexValue = concatHex(normalizedSelectors);
return prepareResult(hexValue, encodingOptions);
}
62 changes: 62 additions & 0 deletions packages/delegation-core/src/caveats/allowedTargets.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import type { BytesLike } from '@metamask/utils';

import { concatHex, normalizeAddress } from '../internalUtils';
import {
defaultOptions,
prepareResult,
type EncodingOptions,
type ResultValue,
} from '../returns';
import type { Hex } from '../types';

/**
* Terms for configuring an AllowedTargets caveat.
*/
export type AllowedTargetsTerms = {
/** An array of target addresses that the delegate is allowed to call. */
targets: BytesLike[];
};

/**
* Creates terms for an AllowedTargets caveat that restricts calls to a set of target addresses.
*
* @param terms - The terms for the AllowedTargets caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as concatenated target addresses.
* @throws Error if the targets array is empty or contains invalid addresses.
*/
export function createAllowedTargetsTerms(
terms: AllowedTargetsTerms,
encodingOptions?: EncodingOptions<'hex'>,
): Hex;
export function createAllowedTargetsTerms(
terms: AllowedTargetsTerms,
encodingOptions: EncodingOptions<'bytes'>,
): Uint8Array;
/**
* Creates terms for an AllowedTargets caveat that restricts calls to a set of target addresses.
*
* @param terms - The terms for the AllowedTargets caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as concatenated target addresses.
* @throws Error if the targets array is empty or contains invalid addresses.
*/
export function createAllowedTargetsTerms(
terms: AllowedTargetsTerms,
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
): Hex | Uint8Array {
const { targets } = terms;

if (!targets || targets.length === 0) {
throw new Error(
'Invalid targets: must provide at least one target address',
);
}

const normalizedTargets = targets.map((target) =>
normalizeAddress(target, 'Invalid targets: must be valid addresses'),
);

const hexValue = concatHex(normalizedTargets);
return prepareResult(hexValue, encodingOptions);
}
60 changes: 60 additions & 0 deletions packages/delegation-core/src/caveats/argsEqualityCheck.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type { BytesLike } from '@metamask/utils';

import { normalizeHex } from '../internalUtils';
import {
defaultOptions,
prepareResult,
type EncodingOptions,
type ResultValue,
} from '../returns';
import type { Hex } from '../types';

/**
* Terms for configuring an ArgsEqualityCheck caveat.
*/
export type ArgsEqualityCheckTerms = {
/** The expected args that must match exactly when redeeming the delegation. */
args: BytesLike;
};

/**
* Creates terms for an ArgsEqualityCheck caveat that requires exact args matching.
*
* @param terms - The terms for the ArgsEqualityCheck caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as the args themselves.
* @throws Error if args is not a valid hex string.
*/
export function createArgsEqualityCheckTerms(
terms: ArgsEqualityCheckTerms,
encodingOptions?: EncodingOptions<'hex'>,
): Hex;
export function createArgsEqualityCheckTerms(
terms: ArgsEqualityCheckTerms,
encodingOptions: EncodingOptions<'bytes'>,
): Uint8Array;
/**
* Creates terms for an ArgsEqualityCheck caveat that requires exact args matching.
*
* @param terms - The terms for the ArgsEqualityCheck caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as the args themselves.
* @throws Error if args is not a valid hex string.
*/
export function createArgsEqualityCheckTerms(
terms: ArgsEqualityCheckTerms,
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
): Hex | Uint8Array {
const { args } = terms;

if (typeof args === 'string' && args === '0x') {
return prepareResult(args, encodingOptions);
}

const hexValue = normalizeHex(
args,
'Invalid config: args must be a valid hex string',
);

return prepareResult(hexValue, encodingOptions);
}
71 changes: 71 additions & 0 deletions packages/delegation-core/src/caveats/blockNumber.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { toHexString } from '../internalUtils';
import {
defaultOptions,
prepareResult,
type EncodingOptions,
type ResultValue,
} from '../returns';
import type { Hex } from '../types';

/**
* Terms for configuring a BlockNumber caveat.
*/
export type BlockNumberTerms = {
/** The block number after which the delegation is valid. Set to 0n to disable. */
afterThreshold: bigint;
/** The block number before which the delegation is valid. Set to 0n to disable. */
beforeThreshold: bigint;
};

/**
* Creates terms for a BlockNumber caveat that constrains delegation validity by block range.
*
* @param terms - The terms for the BlockNumber caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as a 32-byte hex string (16 bytes for each threshold).
* @throws Error if both thresholds are zero or if afterThreshold >= beforeThreshold when both are set.
*/
export function createBlockNumberTerms(
terms: BlockNumberTerms,
encodingOptions?: EncodingOptions<'hex'>,
): Hex;
export function createBlockNumberTerms(
terms: BlockNumberTerms,
encodingOptions: EncodingOptions<'bytes'>,
): Uint8Array;
/**
* Creates terms for a BlockNumber caveat that constrains delegation validity by block range.
*
* @param terms - The terms for the BlockNumber caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as a 32-byte hex string (16 bytes for each threshold).
* @throws Error if both thresholds are zero or if afterThreshold >= beforeThreshold when both are set.
*/
export function createBlockNumberTerms(
terms: BlockNumberTerms,
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
): Hex | Uint8Array {
const { afterThreshold, beforeThreshold } = terms;

if (afterThreshold < 0n || beforeThreshold < 0n) {
throw new Error('Invalid thresholds: block numbers must be non-negative');
}

if (afterThreshold === 0n && beforeThreshold === 0n) {
throw new Error(
'Invalid thresholds: At least one of afterThreshold or beforeThreshold must be specified',
);
}

if (beforeThreshold !== 0n && afterThreshold >= beforeThreshold) {
throw new Error(
'Invalid thresholds: afterThreshold must be less than beforeThreshold if both are specified',
);
}

const afterThresholdHex = toHexString({ value: afterThreshold, size: 16 });
const beforeThresholdHex = toHexString({ value: beforeThreshold, size: 16 });
const hexValue = `0x${afterThresholdHex}${beforeThresholdHex}`;

return prepareResult(hexValue, encodingOptions);
}
Loading
Loading