Skip to content

Commit 884d4a6

Browse files
committed
Ensure that only delegations with inheritance may be created without a scope
1 parent 68ca938 commit 884d4a6

4 files changed

Lines changed: 96 additions & 0 deletions

File tree

packages/smart-accounts-kit/src/caveatBuilder/resolveCaveats.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,24 @@ export type Caveats = CaveatBuilder | (Caveat | CoreCaveatConfiguration)[];
1515
* @param config.environment - The environment to be used for the caveat builder.
1616
* @param config.scope - The scope to be used for the caveat builder. Optional - when not provided and redelegating, scope is inherited from the parent delegation.
1717
* @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.
18+
* @param config.hasInheritance - Whether the delegation has inheritance.
1819
* @returns The resolved array of caveats.
1920
*/
2021
export const resolveCaveats = ({
2122
environment,
2223
scope,
2324
caveats,
25+
hasInheritance,
2426
}: {
2527
environment: SmartAccountsEnvironment;
2628
scope?: ScopeConfig;
2729
caveats?: Caveats;
30+
hasInheritance: boolean;
2831
}) => {
32+
if (!scope && !hasInheritance) {
33+
throw new Error('Scope is required when the delegation has no inheritance');
34+
}
35+
2936
// Create base caveat builder from scope if provided, otherwise use core caveat builder
3037
const scopeCaveatBuilder = scope
3138
? createCaveatBuilderFromScope(environment, scope)

packages/smart-accounts-kit/src/delegation.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,7 @@ export const createDelegation = (
349349
environment: options.environment,
350350
scope: options.scope,
351351
caveats: options.caveats,
352+
hasInheritance: Boolean(parentDelegation),
352353
});
353354

354355
trackSmartAccountsKitFunctionCall('createDelegation', {
@@ -387,6 +388,7 @@ export const createOpenDelegation = (
387388
environment: options.environment,
388389
scope: options.scope,
389390
caveats: options.caveats,
391+
hasInheritance: Boolean(parentDelegation),
390392
});
391393

392394
trackSmartAccountsKitFunctionCall('createOpenDelegation', {

packages/smart-accounts-kit/test/caveatBuilder/resolveCaveats.test.ts

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ describe('resolveCaveats', () => {
4848
environment,
4949
scope: erc20Scope,
5050
caveats: caveatBuilder,
51+
hasInheritance: false,
5152
});
5253

5354
// 4 caveats: 2 from the scope, 2 from the builder
@@ -65,6 +66,7 @@ describe('resolveCaveats', () => {
6566
environment,
6667
scope: erc20Scope,
6768
caveats: caveatBuilder,
69+
hasInheritance: false,
6870
});
6971

7072
expect(result).to.be.an('array');
@@ -83,6 +85,7 @@ describe('resolveCaveats', () => {
8385
environment,
8486
scope: erc20Scope,
8587
caveats: caveatBuilder as any,
88+
hasInheritance: false,
8689
});
8790

8891
expect(result).to.be.an('array');
@@ -98,6 +101,7 @@ describe('resolveCaveats', () => {
98101
environment,
99102
scope: erc20Scope,
100103
caveats,
104+
hasInheritance: false,
101105
});
102106

103107
expect(result).to.be.an('array');
@@ -125,6 +129,7 @@ describe('resolveCaveats', () => {
125129
environment,
126130
scope: erc20Scope,
127131
caveats: caveatConfigs,
132+
hasInheritance: false,
128133
});
129134

130135
expect(result).to.be.an('array');
@@ -135,6 +140,7 @@ describe('resolveCaveats', () => {
135140
environment,
136141
scope: erc20Scope,
137142
caveats: [],
143+
hasInheritance: false,
138144
});
139145
expect(result.length).to.be.greaterThan(scopeOnlyResult.length);
140146
});
@@ -158,6 +164,7 @@ describe('resolveCaveats', () => {
158164
environment,
159165
scope: erc20Scope,
160166
caveats: mixedCaveats,
167+
hasInheritance: false,
161168
});
162169

163170
expect(result).to.be.an('array');
@@ -173,6 +180,7 @@ describe('resolveCaveats', () => {
173180
environment,
174181
scope: erc20Scope,
175182
caveats: [],
183+
hasInheritance: false,
176184
});
177185

178186
expect(result).to.be.an('array');
@@ -197,6 +205,7 @@ describe('resolveCaveats', () => {
197205
environment,
198206
scope: erc721Scope,
199207
caveats: [caveatConfig],
208+
hasInheritance: false,
200209
});
201210

202211
expect(result).to.be.an('array');
@@ -208,12 +217,14 @@ describe('resolveCaveats', () => {
208217
environment,
209218
scope: erc20Scope,
210219
caveats: [mockCaveat1],
220+
hasInheritance: false,
211221
});
212222

213223
const resultWithoutCaveats = resolveCaveats({
214224
environment,
215225
scope: erc20Scope,
216226
caveats: [],
227+
hasInheritance: false,
217228
});
218229

219230
expect(resultWithCaveats.length).to.be.greaterThan(
@@ -236,8 +247,65 @@ describe('resolveCaveats', () => {
236247
environment,
237248
scope: erc20Scope,
238249
caveats: [invalidType as any],
250+
hasInheritance: false,
239251
});
240252
}).to.throw('Invalid caveat');
241253
});
242254
});
255+
256+
describe('hasInheritance', () => {
257+
it('should throw if scope is not provided and the delegation has no inheritance', () => {
258+
expect(() =>
259+
resolveCaveats({
260+
environment,
261+
caveats: [mockCaveat1],
262+
hasInheritance: false,
263+
}),
264+
).to.throw('Scope is required when the delegation has no inheritance');
265+
});
266+
267+
it('should throw if neither scope nor caveats are provided and the delegation has no inheritance', () => {
268+
expect(() =>
269+
resolveCaveats({
270+
environment,
271+
hasInheritance: false,
272+
}),
273+
).to.throw('Scope is required when the delegation has no inheritance');
274+
});
275+
276+
it('should resolve caveats without a scope when the delegation has inheritance', () => {
277+
const result = resolveCaveats({
278+
environment,
279+
caveats: [mockCaveat1, mockCaveat2],
280+
hasInheritance: true,
281+
});
282+
283+
// No scope caveats are added when the scope is inherited from the parent
284+
expect(result).to.have.lengthOf(2);
285+
expect(result).to.deep.include(mockCaveat1);
286+
expect(result).to.deep.include(mockCaveat2);
287+
});
288+
289+
it('should return an empty array when no scope, no caveats and the delegation has inheritance', () => {
290+
const result = resolveCaveats({
291+
environment,
292+
hasInheritance: true,
293+
});
294+
295+
expect(result).to.deep.equal([]);
296+
});
297+
298+
it('should still apply scope caveats when both scope and inheritance are provided', () => {
299+
const result = resolveCaveats({
300+
environment,
301+
scope: erc20Scope,
302+
caveats: [mockCaveat1],
303+
hasInheritance: true,
304+
});
305+
306+
// Scope caveats are still produced when an explicit scope is provided
307+
expect(result.length).to.be.greaterThan(1);
308+
expect(result).to.deep.include(mockCaveat1);
309+
});
310+
});
243311
});

packages/smart-accounts-kit/test/delegation.test.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,16 @@ describe('createDelegation', () => {
353353
});
354354
});
355355

356+
it('throws if no scope no inheritance is provided', () => {
357+
expect(() => {
358+
createDelegation({
359+
environment: smartAccountEnvironment,
360+
to: mockDelegate,
361+
from: mockDelegator,
362+
} as any);
363+
}).toThrow('Scope is required when the delegation has no inheritance');
364+
});
365+
356366
describe('parentPermissionContext support', () => {
357367
it('should create a delegation using parentPermissionContext with Delegation array', () => {
358368
const parentDelegation = createDelegation({
@@ -653,6 +663,15 @@ describe('createOpenDelegation', () => {
653663
});
654664
});
655665

666+
it('throws if no scope no inheritance is provided', () => {
667+
expect(() => {
668+
createOpenDelegation({
669+
environment: smartAccountEnvironment,
670+
from: mockDelegator,
671+
} as any);
672+
}).toThrow('Scope is required when the delegation has no inheritance');
673+
});
674+
656675
describe('parentPermissionContext support', () => {
657676
it('should create an open delegation using parentPermissionContext', () => {
658677
const parentDelegation = createDelegation({

0 commit comments

Comments
 (0)