Skip to content

Commit b43253c

Browse files
authored
Improve safety of CaveatBuilder interface (#24)
* Make caveat builders more secure - rename allowEmptyCaveats to allowInsecureFullAuthorityDelegations - require allowInsecureFullAuthorityDelegations when signing and creating delegations * Update calldata casing to be consistent in delegation-core package * Update CaveatBuilders to accept config parameter. * Fix linting issues * Update e2e tests to match new caveatBuilder interface * Fix references to chai and mocha * Add tsdoc comments to build config types * Fix incorrect type in tokenId comparsion * Remove 0xstring type, fix bigint <> number comparison
1 parent 34fd9c9 commit b43253c

102 files changed

Lines changed: 1409 additions & 839 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/delegation-core/README.md

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ yarn add @metamask/delegation-core
1717
## Overview
1818

1919
This package provides utilities for:
20+
2021
- Creating caveat terms for various delegation constraints
2122
- Encoding and decoding delegations
2223
- Type definitions for delegation structures
@@ -32,33 +33,39 @@ Caveat terms builders create encoded parameters for different types of delegatio
3233
Creates terms for a ValueLte caveat that limits the maximum value of native tokens that can be spent.
3334

3435
**Parameters:**
36+
3537
- `terms: ValueLteTerms`
3638
- `maxValue: bigint` - The maximum value allowed for the transaction
3739
- `options?: EncodingOptions` - Optional encoding options (`'hex'` | `'bytes'`)
3840

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

4143
**Example:**
44+
4245
```typescript
4346
import { createValueLteTerms } from '@metamask/delegation-core';
4447

4548
// Limit to 1 ETH maximum
4649
const terms = createValueLteTerms({
47-
maxValue: 1000000000000000000n // 1 ETH in wei
50+
maxValue: 1000000000000000000n, // 1 ETH in wei
4851
});
4952
// Returns: '0x0000000000000000000000000000000000000000000000000de0b6b3a7640000'
5053

5154
// Get as Uint8Array
52-
const bytesTerms = createValueLteTerms({
53-
maxValue: 1000000000000000000n
54-
}, { out: 'bytes' });
55+
const bytesTerms = createValueLteTerms(
56+
{
57+
maxValue: 1000000000000000000n,
58+
},
59+
{ out: 'bytes' },
60+
);
5561
```
5662

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

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

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

6976
**Example:**
77+
7078
```typescript
7179
import { createTimestampTerms } from '@metamask/delegation-core';
7280

7381
// Valid between Jan 1, 2022 and Jan 1, 2023
7482
const terms = createTimestampTerms({
75-
timestampAfterThreshold: 1640995200, // 2022-01-01 00:00:00 UTC
76-
timestampBeforeThreshold: 1672531200 // 2023-01-01 00:00:00 UTC
83+
timestampAfterThreshold: 1640995200, // 2022-01-01 00:00:00 UTC
84+
timestampBeforeThreshold: 1672531200, // 2023-01-01 00:00:00 UTC
7785
});
7886

7987
// Only valid after a certain time (no end time)
8088
const openEndedTerms = createTimestampTerms({
8189
timestampAfterThreshold: 1640995200,
82-
timestampBeforeThreshold: 0
90+
timestampBeforeThreshold: 0,
8391
});
8492
```
8593

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

9098
**Parameters:**
99+
91100
- `terms: ExactCalldataTerms`
92-
- `callData: BytesLike` - The expected calldata (hex string or Uint8Array)
101+
- `calldata: BytesLike` - The expected calldata (hex string or Uint8Array)
93102
- `options?: EncodingOptions` - Optional encoding options
94103

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

97106
**Example:**
107+
98108
```typescript
99109
import { createExactCalldataTerms } from '@metamask/delegation-core';
100110

101111
// Exact calldata for a specific function call
102112
const terms = createExactCalldataTerms({
103-
callData: '0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d40ec49b0e8baa5e0000000000000000000000000000000000000000000000000de0b6b3a7640000'
113+
calldata:
114+
'0xa9059cbb000000000000000000000000742d35cc6634c0532925a3b8d40ec49b0e8baa5e0000000000000000000000000000000000000000000000000de0b6b3a7640000',
104115
});
105116

106117
// From Uint8Array
107118
const terms2 = createExactCalldataTerms({
108-
callData: new Uint8Array([0xa9, 0x05, 0x9c, 0xbb, /* ... */])
119+
calldata: new Uint8Array([0xa9, 0x05, 0x9c, 0xbb /* ... */]),
109120
});
110121
```
111122

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

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

125137
**Example:**
138+
126139
```typescript
127140
import { createNativeTokenPeriodTransferTerms } from '@metamask/delegation-core';
128141

129142
// Allow 1 ETH per day starting from a specific date
130143
const terms = createNativeTokenPeriodTransferTerms({
131-
periodAmount: 1000000000000000000n, // 1 ETH in wei
132-
periodDuration: 86400, // 24 hours in seconds
133-
startDate: 1640995200 // 2022-01-01 00:00:00 UTC
144+
periodAmount: 1000000000000000000n, // 1 ETH in wei
145+
periodDuration: 86400, // 24 hours in seconds
146+
startDate: 1640995200, // 2022-01-01 00:00:00 UTC
134147
});
135148
```
136149

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

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

151165
**Example:**
166+
152167
```typescript
153168
import { createNativeTokenStreamingTerms } from '@metamask/delegation-core';
154169

155170
// Stream 0.5 ETH per second, starting with 1 ETH, max 10 ETH
156171
const terms = createNativeTokenStreamingTerms({
157-
initialAmount: 1000000000000000000n, // 1 ETH available immediately
158-
maxAmount: 10000000000000000000n, // 10 ETH maximum
159-
amountPerSecond: 500000000000000000n, // 0.5 ETH per second
160-
startTime: 1640995200 // Start streaming at this time
172+
initialAmount: 1000000000000000000n, // 1 ETH available immediately
173+
maxAmount: 10000000000000000000n, // 10 ETH maximum
174+
amountPerSecond: 500000000000000000n, // 0.5 ETH per second
175+
startTime: 1640995200, // Start streaming at this time
161176
});
162177
```
163178

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

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

179195
**Example:**
196+
180197
```typescript
181198
import { createERC20StreamingTerms } from '@metamask/delegation-core';
182199

183200
// Stream USDC tokens
184201
const terms = createERC20StreamingTerms({
185202
tokenAddress: '0xA0b86a33E6441E74C65c6BF2A6d73B895B9b34A2',
186-
initialAmount: 1000000n, // 1 USDC (6 decimals)
187-
maxAmount: 10000000n, // 10 USDC maximum
188-
amountPerSecond: 100000n, // 0.1 USDC per second
189-
startTime: 1640995200
203+
initialAmount: 1000000n, // 1 USDC (6 decimals)
204+
maxAmount: 10000000n, // 10 USDC maximum
205+
amountPerSecond: 100000n, // 0.1 USDC per second
206+
startTime: 1640995200,
190207
});
191208
```
192209

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

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

207225
**Example:**
226+
208227
```typescript
209228
import { createERC20TokenPeriodTransferTerms } from '@metamask/delegation-core';
210229

211230
// Allow 100 tokens per day starting from a specific date
212231
const terms = createERC20TokenPeriodTransferTerms({
213232
tokenAddress: '0xA0b86a33E6441E74C65c6BF2A6d73B895B9b34A2',
214-
periodAmount: 100n, // 100 tokens
215-
periodDuration: 86400, // 24 hours in seconds
216-
startDate: 1640995200 // 2022-01-01 00:00:00 UTC
233+
periodAmount: 100n, // 100 tokens
234+
periodDuration: 86400, // 24 hours in seconds
235+
startDate: 1640995200, // 2022-01-01 00:00:00 UTC
217236
});
218237
```
219238

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

226245
**Parameters:**
246+
227247
- `delegations: Delegation[]` - Array of delegation objects
228248

229249
**Returns:** Encoded delegation data
230250

231251
**Example:**
252+
232253
```typescript
233254
import { encodeDelegations } from '@metamask/delegation-core';
234255

@@ -239,8 +260,8 @@ const delegations = [
239260
authority: '0x...',
240261
caveats: [],
241262
salt: 0n,
242-
signature: '0x...'
243-
}
263+
signature: '0x...',
264+
},
244265
];
245266

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

253274
**Parameters:**
275+
254276
- `data` - Encoded delegation data
255277

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

258280
**Example:**
281+
259282
```typescript
260283
import { decodeDelegations } from '@metamask/delegation-core';
261284

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

269292
**Example:**
293+
270294
```typescript
271295
import { ROOT_AUTHORITY } from '@metamask/delegation-core';
272296

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

280304
**Parameters:**
305+
281306
- `delegation: DelegationStruct` - The delegation object to hash
282307

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

285310
**Example:**
311+
286312
```typescript
287313
import { hashDelegation } from '@metamask/delegation-core';
288314

@@ -292,7 +318,7 @@ const delegation = {
292318
authority: '0x...',
293319
caveats: [],
294320
salt: 0n,
295-
signature: '0x...'
321+
signature: '0x...',
296322
};
297323

298324
const hash = hashDelegation(delegation);
@@ -333,7 +359,7 @@ export type TimestampTerms = {
333359
};
334360

335361
export type ExactCalldataTerms = {
336-
callData: BytesLike;
362+
calldata: BytesLike;
337363
};
338364

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

383409
- [MetaMask Delegation Framework](https://github.com/MetaMask/delegation-framework)
384-
- [EIP-7715: Delegated Authorization](https://eips.ethereum.org/EIPS/eip-7715)
410+
- [EIP-7715: Delegated Authorization](https://eips.ethereum.org/EIPS/eip-7715)

packages/delegation-core/src/caveats/exactCalldata.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import type { Hex } from '../types';
1313
*/
1414
export type ExactCalldataTerms = {
1515
/** The expected calldata to match against. */
16-
callData: BytesLike;
16+
calldata: BytesLike;
1717
};
1818

1919
/**
@@ -23,7 +23,7 @@ export type ExactCalldataTerms = {
2323
* @param terms - The terms for the ExactCalldata caveat.
2424
* @param encodingOptions - The encoding options for the result.
2525
* @returns The terms as the calldata itself.
26-
* @throws Error if the `callData` is invalid.
26+
* @throws Error if the `calldata` is invalid.
2727
*/
2828
export function createExactCalldataTerms(
2929
terms: ExactCalldataTerms,
@@ -39,18 +39,18 @@ export function createExactCalldataTerms(
3939
* @param terms - The terms for the ExactCalldata caveat.
4040
* @param encodingOptions - The encoding options for the result.
4141
* @returns The terms as the calldata itself.
42-
* @throws Error if the `callData` is invalid.
42+
* @throws Error if the `calldata` is invalid.
4343
*/
4444
export function createExactCalldataTerms(
4545
terms: ExactCalldataTerms,
4646
encodingOptions: EncodingOptions<ResultValue> = defaultOptions,
4747
): Hex | Uint8Array {
48-
const { callData } = terms;
48+
const { calldata } = terms;
4949

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

5454
// For exact calldata, the terms are simply the expected calldata
55-
return prepareResult(callData, encodingOptions);
55+
return prepareResult(calldata, encodingOptions);
5656
}

0 commit comments

Comments
 (0)