Skip to content
86 changes: 56 additions & 30 deletions packages/delegation-core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ yarn add @metamask/delegation-core
## Overview

This package provides utilities for:

- Creating caveat terms for various delegation constraints
- Encoding and decoding delegations
- Type definitions for delegation structures
Expand All @@ -32,33 +33,39 @@ Caveat terms builders create encoded parameters for different types of delegatio
Creates terms for a ValueLte caveat that limits the maximum value of native tokens that can be spent.

**Parameters:**

- `terms: ValueLteTerms`
- `maxValue: bigint` - The maximum value allowed for the transaction
- `options?: EncodingOptions` - Optional encoding options (`'hex'` | `'bytes'`)

**Returns:** `Hex | Uint8Array` - 32-byte encoded terms

**Example:**

```typescript
import { createValueLteTerms } from '@metamask/delegation-core';

// Limit to 1 ETH maximum
const terms = createValueLteTerms({
maxValue: 1000000000000000000n // 1 ETH in wei
maxValue: 1000000000000000000n, // 1 ETH in wei
});
// Returns: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000'

// Get as Uint8Array
const bytesTerms = createValueLteTerms({
maxValue: 1000000000000000000n
}, { out: 'bytes' });
const bytesTerms = createValueLteTerms(
{
maxValue: 1000000000000000000n,
},
{ out: 'bytes' },
);
```

#### `createTimestampTerms(terms, options?)`

Creates terms for a Timestamp caveat that enforces time-based constraints on delegation usage.

**Parameters:**

- `terms: TimestampTerms`
- `timestampAfterThreshold: number` - Timestamp (seconds) after which delegation can be used
- `timestampBeforeThreshold: number` - Timestamp (seconds) before which delegation can be used
Expand All @@ -67,19 +74,20 @@ Creates terms for a Timestamp caveat that enforces time-based constraints on del
**Returns:** `Hex | Uint8Array` - 32-byte encoded terms (16 bytes per timestamp)

**Example:**

```typescript
import { createTimestampTerms } from '@metamask/delegation-core';

// Valid between Jan 1, 2022 and Jan 1, 2023
const terms = createTimestampTerms({
timestampAfterThreshold: 1640995200, // 2022-01-01 00:00:00 UTC
timestampBeforeThreshold: 1672531200 // 2023-01-01 00:00:00 UTC
timestampAfterThreshold: 1640995200, // 2022-01-01 00:00:00 UTC
timestampBeforeThreshold: 1672531200, // 2023-01-01 00:00:00 UTC
});

// Only valid after a certain time (no end time)
const openEndedTerms = createTimestampTerms({
timestampAfterThreshold: 1640995200,
timestampBeforeThreshold: 0
timestampBeforeThreshold: 0,
});
```

Expand All @@ -88,24 +96,27 @@ const openEndedTerms = createTimestampTerms({
Creates terms for an ExactCalldata caveat that ensures execution calldata matches exactly.

**Parameters:**

- `terms: ExactCalldataTerms`
- `callData: BytesLike` - The expected calldata (hex string or Uint8Array)
- `calldata: BytesLike` - The expected calldata (hex string or Uint8Array)
- `options?: EncodingOptions` - Optional encoding options

**Returns:** `Hex | Uint8Array` - The calldata itself (variable length)

**Example:**

```typescript
import { createExactCalldataTerms } from '@metamask/delegation-core';

// Exact calldata for a specific function call
const terms = createExactCalldataTerms({
callData: '0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d40ec49b0e8baa5e0000000000000000000000000000000000000000000000000de0b6b3a7640000'
calldata:
'0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d40ec49b0e8baa5e0000000000000000000000000000000000000000000000000de0b6b3a7640000',
});

// From Uint8Array
const terms2 = createExactCalldataTerms({
callData: new Uint8Array([0xa9, 0x05, 0x9c, 0xbb, /* ... */])
calldata: new Uint8Array([0xa9, 0x05, 0x9c, 0xbb /* ... */]),
});
```

Expand All @@ -114,6 +125,7 @@ const terms2 = createExactCalldataTerms({
Creates terms for periodic native token transfer limits with time-based resets.

**Parameters:**

- `terms: NativeTokenPeriodTransferTerms`
- `periodAmount: bigint` - Maximum amount transferable per period (wei)
- `periodDuration: number` - Duration of each period (seconds)
Expand All @@ -123,14 +135,15 @@ Creates terms for periodic native token transfer limits with time-based resets.
**Returns:** `Hex | Uint8Array` - 96-byte encoded terms (32 bytes per parameter)

**Example:**

```typescript
import { createNativeTokenPeriodTransferTerms } from '@metamask/delegation-core';

// Allow 1 ETH per day starting from a specific date
const terms = createNativeTokenPeriodTransferTerms({
periodAmount: 1000000000000000000n, // 1 ETH in wei
periodDuration: 86400, // 24 hours in seconds
startDate: 1640995200 // 2022-01-01 00:00:00 UTC
periodAmount: 1000000000000000000n, // 1 ETH in wei
periodDuration: 86400, // 24 hours in seconds
startDate: 1640995200, // 2022-01-01 00:00:00 UTC
});
```

Expand All @@ -139,6 +152,7 @@ const terms = createNativeTokenPeriodTransferTerms({
Creates terms for linear streaming allowance of native tokens.

**Parameters:**

- `terms: NativeTokenStreamingTerms`
- `initialAmount: bigint` - Amount available immediately (wei)
- `maxAmount: bigint` - Maximum total transferable amount (wei)
Expand All @@ -149,15 +163,16 @@ Creates terms for linear streaming allowance of native tokens.
**Returns:** `Hex | Uint8Array` - 128-byte encoded terms (32 bytes per parameter)

**Example:**

```typescript
import { createNativeTokenStreamingTerms } from '@metamask/delegation-core';

// Stream 0.5 ETH per second, starting with 1 ETH, max 10 ETH
const terms = createNativeTokenStreamingTerms({
initialAmount: 1000000000000000000n, // 1 ETH available immediately
maxAmount: 10000000000000000000n, // 10 ETH maximum
amountPerSecond: 500000000000000000n, // 0.5 ETH per second
startTime: 1640995200 // Start streaming at this time
initialAmount: 1000000000000000000n, // 1 ETH available immediately
maxAmount: 10000000000000000000n, // 10 ETH maximum
amountPerSecond: 500000000000000000n, // 0.5 ETH per second
startTime: 1640995200, // Start streaming at this time
});
```

Expand All @@ -166,6 +181,7 @@ const terms = createNativeTokenStreamingTerms({
Creates terms for linear streaming allowance of ERC20 tokens.

**Parameters:**

- `terms: ERC20StreamingTerms`
- `tokenAddress: string` - ERC20 token contract address
- `initialAmount: bigint` - Amount available immediately
Expand All @@ -177,16 +193,17 @@ Creates terms for linear streaming allowance of ERC20 tokens.
**Returns:** `Hex | Uint8Array` - 148-byte encoded terms (20 bytes + 32 bytes × 4 parameters)

**Example:**

```typescript
import { createERC20StreamingTerms } from '@metamask/delegation-core';

// Stream USDC tokens
const terms = createERC20StreamingTerms({
tokenAddress: '0xA0b86a33E6441E74C65c6BF2A6d73B895B9b34A2',
initialAmount: 1000000n, // 1 USDC (6 decimals)
maxAmount: 10000000n, // 10 USDC maximum
amountPerSecond: 100000n, // 0.1 USDC per second
startTime: 1640995200
initialAmount: 1000000n, // 1 USDC (6 decimals)
maxAmount: 10000000n, // 10 USDC maximum
amountPerSecond: 100000n, // 0.1 USDC per second
startTime: 1640995200,
});
```

Expand All @@ -195,6 +212,7 @@ const terms = createERC20StreamingTerms({
Creates terms for an ERC20TokenPeriodTransfer caveat that validates that ERC20 token transfers do not exceed a specified amount within a given time period.

**Parameters:**

- `terms: ERC20TokenPeriodTransferTerms`
- `tokenAddress: BytesLike` - The address of the ERC20 token.
- `periodAmount: bigint` - The maximum amount that can be transferred within each period.
Expand All @@ -205,15 +223,16 @@ Creates terms for an ERC20TokenPeriodTransfer caveat that validates that ERC20 t
**Returns:** `Hex | Uint8Array` - 116-byte encoded terms (20 bytes for address + 32 bytes per parameter)

**Example:**

```typescript
import { createERC20TokenPeriodTransferTerms } from '@metamask/delegation-core';

// Allow 100 tokens per day starting from a specific date
const terms = createERC20TokenPeriodTransferTerms({
tokenAddress: '0xA0b86a33E6441E74C65c6BF2A6d73B895B9b34A2',
periodAmount: 100n, // 100 tokens
periodDuration: 86400, // 24 hours in seconds
startDate: 1640995200 // 2022-01-01 00:00:00 UTC
periodAmount: 100n, // 100 tokens
periodDuration: 86400, // 24 hours in seconds
startDate: 1640995200, // 2022-01-01 00:00:00 UTC
});
```

Expand All @@ -224,11 +243,13 @@ const terms = createERC20TokenPeriodTransferTerms({
Encodes an array of delegations into a format suitable for on-chain submission.

**Parameters:**

- `delegations: Delegation[]` - Array of delegation objects

**Returns:** Encoded delegation data

**Example:**

```typescript
import { encodeDelegations } from '@metamask/delegation-core';

Expand All @@ -239,8 +260,8 @@ const delegations = [
authority: '0x...',
caveats: [],
salt: 0n,
signature: '0x...'
}
signature: '0x...',
},
];

const encoded = encodeDelegations(delegations);
Expand All @@ -251,11 +272,13 @@ const encoded = encodeDelegations(delegations);
Decodes encoded delegation data back into delegation objects.

**Parameters:**

- `data` - Encoded delegation data

**Returns:** `Delegation[]` - Array of decoded delegation objects

**Example:**

```typescript
import { decodeDelegations } from '@metamask/delegation-core';

Expand All @@ -267,6 +290,7 @@ const delegations = decodeDelegations(encodedData);
A constant representing the root authority in the delegation hierarchy.

**Example:**

```typescript
import { ROOT_AUTHORITY } from '@metamask/delegation-core';

Expand All @@ -278,11 +302,13 @@ console.log(ROOT_AUTHORITY); // Root authority identifier
Computes a hash for a given delegation object.

**Parameters:**

- `delegation: DelegationStruct` - The delegation object to hash

**Returns:** `Hex` - The hash of the delegation

**Example:**

```typescript
import { hashDelegation } from '@metamask/delegation-core';

Expand All @@ -292,7 +318,7 @@ const delegation = {
authority: '0x...',
caveats: [],
salt: 0n,
signature: '0x...'
signature: '0x...',
};

const hash = hashDelegation(delegation);
Expand Down Expand Up @@ -333,7 +359,7 @@ export type TimestampTerms = {
};

export type ExactCalldataTerms = {
callData: BytesLike;
calldata: BytesLike;
};

export type NativeTokenPeriodTransferTerms = {
Expand Down Expand Up @@ -365,7 +391,7 @@ All caveat builders include validation and will throw descriptive errors:
```typescript
try {
const terms = createValueLteTerms({
maxValue: -1n // Invalid: negative value
maxValue: -1n, // Invalid: negative value
});
} catch (error) {
console.error(error.message); // "Invalid maxValue: must be greater than or equal to zero"
Expand All @@ -381,4 +407,4 @@ try {
## Links

- [MetaMask Delegation Framework](https://github.com/MetaMask/delegation-framework)
- [EIP-7715: Delegated Authorization](https://eips.ethereum.org/EIPS/eip-7715)
- [EIP-7715: Delegated Authorization](https://eips.ethereum.org/EIPS/eip-7715)
14 changes: 7 additions & 7 deletions packages/delegation-core/src/caveats/exactCalldata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import type { Hex } from '../types';
*/
export type ExactCalldataTerms = {
/** The expected calldata to match against. */
callData: BytesLike;
calldata: BytesLike;
};

/**
Expand All @@ -23,7 +23,7 @@ export type ExactCalldataTerms = {
* @param terms - The terms for the ExactCalldata caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as the calldata itself.
* @throws Error if the `callData` is invalid.
* @throws Error if the `calldata` is invalid.
*/
export function createExactCalldataTerms(
terms: ExactCalldataTerms,
Expand All @@ -39,18 +39,18 @@ export function createExactCalldataTerms(
* @param terms - The terms for the ExactCalldata caveat.
* @param encodingOptions - The encoding options for the result.
* @returns The terms as the calldata itself.
* @throws Error if the `callData` is invalid.
* @throws Error if the `calldata` is invalid.
*/
export function createExactCalldataTerms(
terms: ExactCalldataTerms,
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
): Hex | Uint8Array {
const { callData } = terms;
const { calldata } = terms;

if (typeof callData === 'string' && !callData.startsWith('0x')) {
throw new Error('Invalid callData: must be a hex string starting with 0x');
if (typeof calldata === 'string' && !calldata.startsWith('0x')) {
throw new Error('Invalid calldata: must be a hex string starting with 0x');
}

// For exact calldata, the terms are simply the expected calldata
return prepareResult(callData, encodingOptions);
return prepareResult(calldata, encodingOptions);
}
Loading
Loading