Skip to content

Commit 6273f37

Browse files
committed
If parent delegation is an opendelegation, from should be the signer
1 parent cb28b09 commit 6273f37

3 files changed

Lines changed: 91 additions & 18 deletions

File tree

packages/smart-accounts-kit/src/actions/redelegatePermissionContext.ts

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
createOpenDelegation,
1919
decodeDelegations,
2020
encodeDelegations,
21+
ROOT_AUTHORITY,
2122
} from '../delegation';
2223
import type {
2324
Delegation,
@@ -99,7 +100,7 @@ async function signAndPrependRedelegation(
99100
delegationManager: environment.DelegationManager,
100101
chainId,
101102
// Redelegations always inherit from a parent delegation (enforced by
102-
// `resolveRedelegationInputs`), so the parent's caveats provide the
103+
// `resolveRedelegationArgs`), so the parent's caveats provide the
103104
// restriction even when the redelegation itself adds no extra caveats.
104105
// This mirrors `resolveCaveats`, which also allows empty caveats in this
105106
// case.
@@ -129,7 +130,7 @@ async function signAndPrependRedelegation(
129130
* @param parameters - The base redelegation parameters.
130131
* @returns The resolved account, decoded chain and parent (leaf) delegation.
131132
*/
132-
function resolveRedelegationInputs<
133+
function resolveRedelegationArgs<
133134
TChain extends Chain | undefined,
134135
TAccount extends Account | undefined,
135136
>(
@@ -139,6 +140,7 @@ function resolveRedelegationInputs<
139140
account: Account;
140141
delegations: Delegation[];
141142
parentDelegation: Delegation;
143+
from: Hex;
142144
} {
143145
const { account: accountParam = client.account, permissionContext } =
144146
parameters;
@@ -157,7 +159,14 @@ function resolveRedelegationInputs<
157159
);
158160
}
159161

160-
return { account, delegations, parentDelegation };
162+
const isParentOpenDelegation =
163+
parentDelegation.authority.toLowerCase() === ROOT_AUTHORITY.toLowerCase();
164+
165+
const from = isParentOpenDelegation
166+
? account.address
167+
: parentDelegation.delegate;
168+
169+
return { account, delegations, parentDelegation, from };
161170
}
162171

163172
/**
@@ -208,10 +217,8 @@ export async function redelegatePermissionContext<
208217
): Promise<RedelegatePermissionContextReturnType> {
209218
const { environment, chainId, scope, caveats, salt, to } = parameters;
210219

211-
const { account, delegations, parentDelegation } = resolveRedelegationInputs(
212-
client,
213-
parameters,
214-
);
220+
const { account, delegations, parentDelegation, from } =
221+
resolveRedelegationArgs(client, parameters);
215222

216223
trackSmartAccountsKitFunctionCall('redelegatePermissionContext', {
217224
chainId,
@@ -221,7 +228,7 @@ export async function redelegatePermissionContext<
221228

222229
const unsignedDelegation = createDelegation({
223230
environment,
224-
from: parentDelegation.delegate,
231+
from,
225232
to,
226233
scope,
227234
caveats,
@@ -269,10 +276,8 @@ export async function redelegatePermissionContextOpen<
269276
): Promise<RedelegatePermissionContextReturnType> {
270277
const { environment, chainId, scope, caveats, salt } = parameters;
271278

272-
const { account, delegations, parentDelegation } = resolveRedelegationInputs(
273-
client,
274-
parameters,
275-
);
279+
const { account, delegations, parentDelegation, from } =
280+
resolveRedelegationArgs(client, parameters);
276281

277282
trackSmartAccountsKitFunctionCall('redelegatePermissionContextOpen', {
278283
chainId,
@@ -282,7 +287,7 @@ export async function redelegatePermissionContextOpen<
282287

283288
const unsignedDelegation = createOpenDelegation({
284289
environment,
285-
from: parentDelegation.delegate,
290+
from,
286291
scope,
287292
caveats,
288293
parentDelegation,

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

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -336,14 +336,20 @@ const resolveDelegationArgs = (
336336
const authority = (() => {
337337
if (!resolvedParentDelegation) {
338338
if (!options.scope) {
339-
throw new Error('Invalid arguments - must specify either parentDelegation, parentPermissionContext, or scope');
339+
throw new Error(
340+
'Invalid arguments - must specify either parentDelegation, parentPermissionContext, or scope',
341+
);
340342
}
341343
return ROOT_AUTHORITY;
342344
}
343345

344346
if (typeof resolvedParentDelegation === 'string') {
345-
if (resolvedParentDelegation.toLowerCase() === ROOT_AUTHORITY.toLowerCase()) {
346-
throw new Error(`Invalid parent delegation - cannot be ${ROOT_AUTHORITY}`);
347+
if (
348+
resolvedParentDelegation.toLowerCase() === ROOT_AUTHORITY.toLowerCase()
349+
) {
350+
throw new Error(
351+
`Invalid parent delegation - cannot be ${ROOT_AUTHORITY}`,
352+
);
347353
}
348354
return resolvedParentDelegation;
349355
}

packages/smart-accounts-kit/test/actions/redelegatePermissionContext.test.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import {
99
redelegatePermissionContextActions,
1010
} from '../../src/actions/redelegatePermissionContext';
1111
import { ScopeType } from '../../src/constants';
12-
import { createDelegation, encodeDelegations } from '../../src/delegation';
12+
import {
13+
createDelegation,
14+
createOpenDelegation,
15+
encodeDelegations,
16+
} from '../../src/delegation';
1317
import type { SmartAccountsEnvironment } from '../../src/types';
1418

1519
const mockPrivateKey =
@@ -74,7 +78,35 @@ const buildPermissionContextWithParentDelegate = (
7478
from: '0x1000000000000000000000000000000000000001',
7579
});
7680

77-
return encodeDelegations([rootDelegation]);
81+
const leafDelegation = createDelegation({
82+
environment: mockEnvironment,
83+
scope: {
84+
type: ScopeType.Erc20TransferAmount,
85+
tokenAddress: '0xabc0000000000000000000000000000000000000',
86+
maxAmount,
87+
},
88+
to: parentDelegate,
89+
from: '0x2000000000000000000000000000000000000002',
90+
parentDelegation: rootDelegation,
91+
});
92+
93+
return encodeDelegations([leafDelegation, rootDelegation]);
94+
};
95+
96+
const buildPermissionContextWithOpenLeafDelegation = ({
97+
maxAmount = 1000n,
98+
}: { maxAmount?: bigint } = {}) => {
99+
const openDelegation = createOpenDelegation({
100+
environment: mockEnvironment,
101+
scope: {
102+
type: ScopeType.Erc20TransferAmount,
103+
tokenAddress: '0xabc0000000000000000000000000000000000000',
104+
maxAmount,
105+
},
106+
from: '0x1000000000000000000000000000000000000001',
107+
});
108+
109+
return encodeDelegations([openDelegation]);
78110
};
79111

80112
describe('redelegatePermissionContext', () => {
@@ -188,6 +220,22 @@ describe('redelegatePermissionContext', () => {
188220
expect(result.delegation.delegator).to.not.equal(account.address);
189221
});
190222

223+
it('should use the account address as from when the leaf delegation is open', async () => {
224+
const permissionContext = buildPermissionContextWithOpenLeafDelegation();
225+
const newDelegate: Address = '0x2000000000000000000000000000000000000002';
226+
227+
const result = await redelegatePermissionContext(client, {
228+
environment: mockEnvironment,
229+
permissionContext,
230+
chainId: mockChainId,
231+
to: newDelegate,
232+
});
233+
234+
expect(result.delegation.delegator.toLowerCase()).to.equal(
235+
account.address.toLowerCase(),
236+
);
237+
});
238+
191239
it('should allow scope override even with parent', async () => {
192240
const permissionContext = buildRootPermissionContext();
193241
const newDelegate: Address = '0x2000000000000000000000000000000000000002';
@@ -360,6 +408,20 @@ describe('redelegatePermissionContextOpen', () => {
360408
expect(result.delegation.delegator).to.not.equal(account.address);
361409
});
362410

411+
it('should use the account address as from when the leaf delegation is open', async () => {
412+
const permissionContext = buildPermissionContextWithOpenLeafDelegation();
413+
414+
const result = await redelegatePermissionContextOpen(client, {
415+
environment: mockEnvironment,
416+
permissionContext,
417+
chainId: mockChainId,
418+
});
419+
420+
expect(result.delegation.delegator.toLowerCase()).to.equal(
421+
account.address.toLowerCase(),
422+
);
423+
});
424+
363425
it('should allow scope override on an open redelegation', async () => {
364426
const permissionContext = buildRootPermissionContext();
365427

0 commit comments

Comments
 (0)