Skip to content

Commit 22597f3

Browse files
committed
refactor: improve declarative delegation interface
1 parent a22ecb5 commit 22597f3

24 files changed

Lines changed: 1293 additions & 94 deletions

packages/delegation-toolkit/src/caveatBuilder/resolveCaveats.ts

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type Caveats = CaveatBuilder | (Caveat | CoreCaveatConfiguration)[];
1010
* @param config - The configuration for the caveat builder.
1111
* @param config.environment - The environment to be used for the caveat builder.
1212
* @param config.scope - The scope to be used for the caveat builder.
13-
* @param config.caveats - The caveats to be resolved, which can be either a CaveatBuilder or an array of Caveat or CaveatConfiguration.
13+
* @param config.caveats - The caveats to be resolved, which can be either a CaveatBuilder or an array of Caveat or CaveatConfiguration. Optional - if not provided, only scope caveats will be used.
1414
* @returns The resolved array of caveats.
1515
*/
1616
export const resolveCaveats = ({
@@ -20,27 +20,29 @@ export const resolveCaveats = ({
2020
}: {
2121
environment: DeleGatorEnvironment;
2222
scope: ScopeConfig;
23-
caveats: Caveats;
23+
caveats?: Caveats;
2424
}) => {
2525
const scopeCaveatBuilder = createCaveatBuilderFromScope(environment, scope);
2626

27-
if ('build' in caveats && typeof caveats.build === 'function') {
28-
(caveats as CaveatBuilder).build().forEach((caveat) => {
29-
scopeCaveatBuilder.addCaveat(caveat);
30-
});
31-
} else if (Array.isArray(caveats)) {
32-
caveats.forEach((caveat) => {
33-
try {
34-
if ('type' in caveat) {
35-
const { type, ...config } = caveat;
36-
scopeCaveatBuilder.addCaveat(type, config);
37-
} else {
38-
scopeCaveatBuilder.addCaveat(caveat);
27+
if (caveats) {
28+
if ('build' in caveats && typeof caveats.build === 'function') {
29+
(caveats as CaveatBuilder).build().forEach((caveat) => {
30+
scopeCaveatBuilder.addCaveat(caveat);
31+
});
32+
} else if (Array.isArray(caveats)) {
33+
caveats.forEach((caveat) => {
34+
try {
35+
if ('type' in caveat) {
36+
const { type, ...config } = caveat;
37+
scopeCaveatBuilder.addCaveat(type, config);
38+
} else {
39+
scopeCaveatBuilder.addCaveat(caveat);
40+
}
41+
} catch (error) {
42+
throw new Error(`Invalid caveat: ${(error as Error).message}`);
3943
}
40-
} catch (error) {
41-
throw new Error(`Invalid caveat: ${(error as Error).message}`);
42-
}
43-
});
44+
});
45+
}
4446
}
4547

4648
return scopeCaveatBuilder.build();

packages/delegation-toolkit/src/caveatBuilder/scope/nativeTokenPeriodicScope.ts

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import type { DeleGatorEnvironment } from '../../types';
2+
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
23
import { createCaveatBuilder } from '../coreCaveatBuilder';
34
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
5+
import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
46
import type {
57
nativeTokenPeriodTransfer,
68
NativeTokenPeriodTransferBuilderConfig,
79
} from '../nativeTokenPeriodTransferBuilder';
810

911
export type NativeTokenPeriodicScopeConfig = {
1012
type: typeof nativeTokenPeriodTransfer;
13+
allowedCalldata?: AllowedCalldataBuilderConfig[];
14+
exactCalldata?: ExactCalldataBuilderConfig;
1115
} & NativeTokenPeriodTransferBuilderConfig;
1216

1317
/**
@@ -23,13 +27,36 @@ export function createNativeTokenPeriodicCaveatBuilder(
2327
environment: DeleGatorEnvironment,
2428
config: NativeTokenPeriodicScopeConfig,
2529
): CoreCaveatBuilder {
26-
return createCaveatBuilder(environment)
27-
.addCaveat('exactCalldata', {
30+
const {
31+
periodAmount,
32+
periodDuration,
33+
startDate,
34+
allowedCalldata,
35+
exactCalldata,
36+
} = config;
37+
38+
const caveatBuilder = createCaveatBuilder(environment);
39+
40+
// Add calldata restrictions
41+
if (allowedCalldata) {
42+
allowedCalldata.forEach((calldataConfig) => {
43+
caveatBuilder.addCaveat('allowedCalldata', calldataConfig);
44+
});
45+
} else if (exactCalldata) {
46+
caveatBuilder.addCaveat('exactCalldata', exactCalldata);
47+
} else {
48+
// Default behavior: only allow empty calldata
49+
caveatBuilder.addCaveat('exactCalldata', {
2850
calldata: '0x',
29-
})
30-
.addCaveat('nativeTokenPeriodTransfer', {
31-
periodAmount: config.periodAmount,
32-
periodDuration: config.periodDuration,
33-
startDate: config.startDate,
3451
});
52+
}
53+
54+
// Add native token period transfer restriction
55+
caveatBuilder.addCaveat('nativeTokenPeriodTransfer', {
56+
periodAmount,
57+
periodDuration,
58+
startDate,
59+
});
60+
61+
return caveatBuilder;
3562
}
Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import type { DeleGatorEnvironment } from '../../types';
2+
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
23
import { createCaveatBuilder } from '../coreCaveatBuilder';
34
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
5+
import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
46
import type {
57
nativeTokenStreaming,
68
NativeTokenStreamingBuilderConfig,
79
} from '../nativeTokenStreamingBuilder';
810

911
export type NativeTokenStreamingScopeConfig = {
1012
type: typeof nativeTokenStreaming;
13+
allowedCalldata?: AllowedCalldataBuilderConfig[];
14+
exactCalldata?: ExactCalldataBuilderConfig;
1115
} & NativeTokenStreamingBuilderConfig;
1216

1317
/**
@@ -23,14 +27,38 @@ export function createNativeTokenStreamingCaveatBuilder(
2327
environment: DeleGatorEnvironment,
2428
config: NativeTokenStreamingScopeConfig,
2529
): CoreCaveatBuilder {
26-
return createCaveatBuilder(environment)
27-
.addCaveat('exactCalldata', {
30+
const {
31+
initialAmount,
32+
maxAmount,
33+
amountPerSecond,
34+
startTime,
35+
allowedCalldata,
36+
exactCalldata,
37+
} = config;
38+
39+
const caveatBuilder = createCaveatBuilder(environment);
40+
41+
// Add calldata restrictions
42+
if (allowedCalldata) {
43+
allowedCalldata.forEach((calldataConfig) => {
44+
caveatBuilder.addCaveat('allowedCalldata', calldataConfig);
45+
});
46+
} else if (exactCalldata) {
47+
caveatBuilder.addCaveat('exactCalldata', exactCalldata);
48+
} else {
49+
// Default behavior: only allow empty calldata
50+
caveatBuilder.addCaveat('exactCalldata', {
2851
calldata: '0x',
29-
})
30-
.addCaveat('nativeTokenStreaming', {
31-
initialAmount: config.initialAmount,
32-
maxAmount: config.maxAmount,
33-
amountPerSecond: config.amountPerSecond,
34-
startTime: config.startTime,
3552
});
53+
}
54+
55+
// Add native token streaming restriction
56+
caveatBuilder.addCaveat('nativeTokenStreaming', {
57+
initialAmount,
58+
maxAmount,
59+
amountPerSecond,
60+
startTime,
61+
});
62+
63+
return caveatBuilder;
3664
}

packages/delegation-toolkit/src/caveatBuilder/scope/nativeTokenTransferScope.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import type { DeleGatorEnvironment } from '../../types';
2+
import type { AllowedCalldataBuilderConfig } from '../allowedCalldataBuilder';
23
import { createCaveatBuilder } from '../coreCaveatBuilder';
34
import type { CoreCaveatBuilder } from '../coreCaveatBuilder';
5+
import type { ExactCalldataBuilderConfig } from '../exactCalldataBuilder';
46
import type {
57
nativeTokenTransferAmount,
68
NativeTokenTransferAmountBuilderConfig,
79
} from '../nativeTokenTransferAmountBuilder';
810

911
export type NativeTokenTransferScopeConfig = {
1012
type: typeof nativeTokenTransferAmount;
13+
allowedCalldata?: AllowedCalldataBuilderConfig[];
14+
exactCalldata?: ExactCalldataBuilderConfig;
1115
} & NativeTokenTransferAmountBuilderConfig;
1216

1317
/**
@@ -23,11 +27,28 @@ export function createNativeTokenTransferCaveatBuilder(
2327
environment: DeleGatorEnvironment,
2428
config: NativeTokenTransferScopeConfig,
2529
): CoreCaveatBuilder {
26-
return createCaveatBuilder(environment)
27-
.addCaveat('exactCalldata', {
30+
const { maxAmount, allowedCalldata, exactCalldata } = config;
31+
32+
const caveatBuilder = createCaveatBuilder(environment);
33+
34+
// Add calldata restrictions
35+
if (allowedCalldata) {
36+
allowedCalldata.forEach((calldataConfig) => {
37+
caveatBuilder.addCaveat('allowedCalldata', calldataConfig);
38+
});
39+
} else if (exactCalldata) {
40+
caveatBuilder.addCaveat('exactCalldata', exactCalldata);
41+
} else {
42+
// Default behavior: only allow empty calldata
43+
caveatBuilder.addCaveat('exactCalldata', {
2844
calldata: '0x',
29-
})
30-
.addCaveat('nativeTokenTransferAmount', {
31-
maxAmount: config.maxAmount,
3245
});
46+
}
47+
48+
// Add native token transfer amount restriction
49+
caveatBuilder.addCaveat('nativeTokenTransferAmount', {
50+
maxAmount,
51+
});
52+
53+
return caveatBuilder;
3354
}

packages/delegation-toolkit/src/delegation.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -193,10 +193,9 @@ type BaseCreateDelegationOptions = {
193193
environment: DeleGatorEnvironment;
194194
scope: ScopeConfig;
195195
from: Hex;
196-
caveats: Caveats;
196+
caveats?: Caveats;
197197
parentDelegation?: Delegation | Hex;
198198
salt?: Hex;
199-
allowInsecureUnrestrictedDelegation?: boolean;
200199
};
201200

202201
/**

packages/delegation-toolkit/test/DelegationFramework/DelegationManager/delegationManagement.test.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,6 @@ describe('DelegationManager - Delegation Management', () => {
9191
targets: [alice.address],
9292
selectors: ['0x00000000'],
9393
},
94-
caveats: [],
9594
});
9695

9796
const encodedData = DelegationManager.encode.disableDelegation({
@@ -117,7 +116,6 @@ describe('DelegationManager - Delegation Management', () => {
117116
targets: [alice.address],
118117
selectors: ['0x00000000'],
119118
},
120-
caveats: [],
121119
});
122120

123121
const encodedData = DelegationManager.encode.enableDelegation({
@@ -143,7 +141,6 @@ describe('DelegationManager - Delegation Management', () => {
143141
targets: [alice.address],
144142
selectors: ['0x00000000'],
145143
},
146-
caveats: [],
147144
});
148145

149146
const execution = createExecution({

packages/delegation-toolkit/test/caveatBuilder/scope/nativeTokenTransferScope.test.ts

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,12 @@ describe('createNativeTokenTransferCaveatBuilder', () => {
1010
const environment = {
1111
caveatEnforcers: {
1212
ExactCalldataEnforcer: randomAddress(),
13+
AllowedCalldataEnforcer: randomAddress(),
1314
NativeTokenTransferAmountEnforcer: randomAddress(),
1415
},
1516
} as unknown as DeleGatorEnvironment;
1617

17-
it('creates a native token transfer CaveatBuilder', () => {
18+
it('creates a native token transfer CaveatBuilder with default empty calldata', () => {
1819
const config: NativeTokenTransferScopeConfig = {
1920
type: 'nativeTokenTransferAmount',
2021
maxAmount: 1000n,
@@ -40,4 +41,69 @@ describe('createNativeTokenTransferCaveatBuilder', () => {
4041
},
4142
]);
4243
});
44+
45+
it('creates a native token transfer CaveatBuilder with exact calldata', () => {
46+
const config: NativeTokenTransferScopeConfig = {
47+
type: 'nativeTokenTransferAmount',
48+
maxAmount: 1000n,
49+
exactCalldata: {
50+
calldata: '0x1234abcd',
51+
},
52+
};
53+
54+
const caveatBuilder = createNativeTokenTransferCaveatBuilder(
55+
environment,
56+
config,
57+
);
58+
59+
const caveats = caveatBuilder.build();
60+
61+
expect(caveats).to.deep.equal([
62+
{
63+
enforcer: environment.caveatEnforcers.ExactCalldataEnforcer,
64+
args: '0x',
65+
terms: '0x1234abcd',
66+
},
67+
{
68+
enforcer: environment.caveatEnforcers.NativeTokenTransferAmountEnforcer,
69+
args: '0x',
70+
terms: toHex(config.maxAmount, { size: 32 }),
71+
},
72+
]);
73+
});
74+
75+
it('creates a native token transfer CaveatBuilder with allowed calldata', () => {
76+
const config: NativeTokenTransferScopeConfig = {
77+
type: 'nativeTokenTransferAmount',
78+
maxAmount: 1000n,
79+
allowedCalldata: [
80+
{
81+
startIndex: 4,
82+
value: '0x1234',
83+
},
84+
{
85+
startIndex: 8,
86+
value: '0xabcd',
87+
},
88+
],
89+
};
90+
91+
const caveatBuilder = createNativeTokenTransferCaveatBuilder(
92+
environment,
93+
config,
94+
);
95+
96+
const caveats = caveatBuilder.build();
97+
98+
expect(caveats).to.have.lengthOf(3);
99+
expect(caveats[0]?.enforcer).to.equal(
100+
environment.caveatEnforcers.AllowedCalldataEnforcer,
101+
);
102+
expect(caveats[1]?.enforcer).to.equal(
103+
environment.caveatEnforcers.AllowedCalldataEnforcer,
104+
);
105+
expect(caveats[2]?.enforcer).to.equal(
106+
environment.caveatEnforcers.NativeTokenTransferAmountEnforcer,
107+
);
108+
});
43109
});

0 commit comments

Comments
 (0)