Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ let aliceSmartAccount: MetaMaskSmartAccount<Implementation.Hybrid>;
let bob: Account;
let bobPrivateKey: Hex;
let aliceCounterContractAddress: Address;
let permissionsContext: Hex;
let permissionContext: Hex;
let signedDelegation: Delegation;

beforeEach(async () => {
Expand Down Expand Up @@ -75,7 +75,7 @@ beforeEach(async () => {
signature: await aliceSmartAccount.signDelegation({ delegation }),
};

permissionsContext = encodeDelegations([signedDelegation]);
permissionContext = encodeDelegations([signedDelegation]);
});

/*
Expand Down Expand Up @@ -113,7 +113,7 @@ test('maincase: Bob redeems the delegation in order to increment() on the counte
abi: CounterMetadata.abi,
functionName: 'increment',
}),
permissionsContext,
permissionContext,
delegationManager,
});

Expand Down Expand Up @@ -183,7 +183,7 @@ test('Bob redelegates to Carol, who redeems the delegation to call increment() o
abi: CounterMetadata.abi,
functionName: 'increment',
}),
permissionsContext: redelegatedPermissionsContext,
permissionContext: redelegatedPermissionsContext,
delegationManager,
},
);
Expand Down Expand Up @@ -253,7 +253,7 @@ test('Bob sends a native value transaction with delegation', async () => {
signature: await aliceSmartAccount.signDelegation({ delegation }),
};

const permissionsContext = encodeDelegations([signedDelegation]);
const permissionContext = encodeDelegations([signedDelegation]);

const bobWalletClient = createWalletClient({
account: bob,
Expand All @@ -268,7 +268,7 @@ test('Bob sends a native value transaction with delegation', async () => {
chain,
to: recipient,
value: maxAmount,
permissionsContext,
permissionContext,
delegationManager,
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ import { expectUserOperationToSucceed } from '../utils/assertions';
let aliceSmartAccount: MetaMaskSmartAccount<Implementation.Hybrid>;
let bobSmartAccount: MetaMaskSmartAccount<Implementation.Hybrid>;
let aliceCounterContractAddress: Address;
let permissionsContext: Hex;
let permissionContext: Hex;
let signedDelegation: Delegation;

const bundlerClient = sponsoredBundlerClient.extend(erc7710BundlerActions());
Expand Down Expand Up @@ -81,7 +81,7 @@ beforeEach(async () => {
signature: await aliceSmartAccount.signDelegation({ delegation }),
};

permissionsContext = encodeDelegations([signedDelegation]);
permissionContext = encodeDelegations([signedDelegation]);
});

/*
Expand Down Expand Up @@ -114,7 +114,7 @@ test('maincase: Bob redeems the delegation in order to call increment() on the c
functionName: 'increment',
}),
value: 0n,
permissionsContext,
permissionContext,
delegationManager: aliceSmartAccount.environment.DelegationManager,
},
],
Expand Down Expand Up @@ -158,7 +158,7 @@ test('Bob redeems the delegation in order to call increment() on the counter con
functionName: 'increment',
}),
value: 0n,
permissionsContext,
permissionContext,
delegationManager: aliceSmartAccount.environment.DelegationManager,
},
{
Expand Down Expand Up @@ -217,7 +217,7 @@ test('Bob redeems the delegation, and deploys Alices smart account via accountMe
functionName: 'increment',
}),
value: 0n,
permissionsContext,
permissionContext,
delegationManager: aliceSmartAccount.environment.DelegationManager,
},
],
Expand Down Expand Up @@ -268,7 +268,7 @@ test('Bob redeems the delegation, with account metadata, even though Alices acco
functionName: 'increment',
}),
value: 0n,
permissionsContext,
permissionContext,
delegationManager: aliceSmartAccount.environment.DelegationManager,
},
],
Expand Down
32 changes: 16 additions & 16 deletions packages/delegator-e2e/test/caveats/nativeTokenPayment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,11 +120,11 @@ test('maincase: Bob redeems the delegation with a permissions context allowing p
}),
};

const permissionsContext = encodeDelegations([signedPaymentDelegation]);
const permissionContext = encodeDelegations([signedPaymentDelegation]);

await runTest_expectSuccess(
delegationRequiringNativeTokenPayment,
permissionsContext,
permissionContext,
recipient,
requiredValue,
);
Expand Down Expand Up @@ -167,11 +167,11 @@ test('Bob attempts to redeem the delegation without an argsEqualityCheckEnforcer
}),
};

const permissionsContext = encodeDelegations([signedPaymentDelegation]);
const permissionContext = encodeDelegations([signedPaymentDelegation]);

await runTest_expectFailure(
delegationRequiringNativeTokenPayment,
permissionsContext,
permissionContext,
recipient,
'NativeTokenPaymentEnforcer:missing-argsEqualityCheckEnforcer',
);
Expand All @@ -195,11 +195,11 @@ test('Bob attempts to redeem the delegation without providing a valid permission
signature: '0x',
};

const permissionsContext = '0x' as const;
const permissionContext = '0x' as const;

await runTest_expectFailure(
delegationRequiringNativeTokenPayment,
permissionsContext,
permissionContext,
recipient,
undefined, // The NativeTokenPaymentEnforcer rejects when it fails to decode the permissions context
);
Expand Down Expand Up @@ -256,11 +256,11 @@ test('Bob attempts to redeem with invalid terms length', async () => {
}),
};

const permissionsContext = encodeDelegations([signedPaymentDelegation]);
const permissionContext = encodeDelegations([signedPaymentDelegation]);

await runTest_expectFailure(
delegationRequiringNativeTokenPayment,
permissionsContext,
permissionContext,
recipient,
'NativeTokenPaymentEnforcer:invalid-terms-length',
);
Expand All @@ -286,27 +286,27 @@ test('Bob attempts to redeem with empty allowance delegations', async () => {
};

// Create empty allowance delegations array
const permissionsContext = encodeDelegations([]);
const permissionContext = encodeDelegations([]);

await runTest_expectFailure(
delegationRequiringNativeTokenPayment,
permissionsContext,
permissionContext,
recipient,
'NativeTokenPaymentEnforcer:invalid-allowance-delegations-length',
);
});

const runTest_expectSuccess = async (
delegation: Delegation,
permissionsContext: Hex,
permissionContext: Hex,
recipient: Address,
requiredValue: bigint,
) => {
const balanceBefore = await publicClient.getBalance({
address: recipient,
});

const userOpHash = await submitUserOpForTest(delegation, permissionsContext);
const userOpHash = await submitUserOpForTest(delegation, permissionContext);

const receipt = await sponsoredBundlerClient.waitForUserOperationReceipt({
hash: userOpHash,
Expand All @@ -326,7 +326,7 @@ const runTest_expectSuccess = async (

const runTest_expectFailure = async (
delegation: Delegation,
permissionsContext: Hex,
permissionContext: Hex,
recipient: Address,
expectedError: string | undefined,
) => {
Expand All @@ -335,7 +335,7 @@ const runTest_expectFailure = async (
});

const rejects = expect(
submitUserOpForTest(delegation, permissionsContext),
submitUserOpForTest(delegation, permissionContext),
).rejects;

if (expectedError) {
Expand All @@ -355,7 +355,7 @@ const runTest_expectFailure = async (

const submitUserOpForTest = async (
delegation: Delegation,
permissionsContext: Hex,
permissionContext: Hex,
) => {
const signedDelegation = {
...delegation,
Expand All @@ -364,7 +364,7 @@ const submitUserOpForTest = async (

// we need to assign the permissions context to the caveat in order for it to process the payment
// here we assume that the first caveat is the nativeTokenPayment caveat
signedDelegation.caveats[0].args = permissionsContext;
signedDelegation.caveats[0].args = permissionContext;

const execution = createExecution({
target: zeroAddress,
Expand Down
10 changes: 10 additions & 0 deletions packages/smart-accounts-kit/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Changed

- Introduce `PermissionContext` to represent a delegation chain (ABI-encoded `Hex` or decoded `Delegation[]`). ([#140](https://github.com/MetaMask/smart-accounts-kit/pull/140))
- **Breaking**: Replace usages of raw `Hex` _or_ `Delegation[]` with `PermissionContext`, and rename `permissionsContext` to `permissionContext` (note the singular "permission") where applicable:
- `SendTransactionWithDelegation`: `permissionsContext: Hex` β†’ `permissionContext: PermissionContext`
- `SendUserOperationWithDelegation`: within `calls: DelegatedCall`, `permissionsContext: Hex` β†’ `permissionContext: PermissionContext`
- `redeemDelegations`: parameter `Delegation[]` β†’ `PermissionContext`
- `encodeDelegations` and `decodeDelegations` now accept `PermissionContext` (if the input is already the expected type, the input is returned)
- `encode`, `execute`, and `simulate` functions for `DelegationManager.redeemDelegations` from `@metamask/smart-accounts-kit/contracts`: parameter `delegations: Delegation[]` β†’ `delegations: PermissionContext`

### Fixed

- Allow scope type to be specified either as `ScopeType` enum, or string literal ([#133](https://github.com/MetaMask/smart-accounts-kit/pull/133))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,14 @@ import type { Address, Client } from 'viem';
import { encodeFunctionData } from 'viem';
import { simulateContract, writeContract } from 'viem/actions';

import { encodePermissionContexts } from '../../../delegation';
import { encodeDelegations } from '../../../delegation';
import { encodeExecutionCalldatas } from '../../../executions';
import type { ExecutionMode, ExecutionStruct } from '../../../executions';
import type { Delegation } from '../../../types';
import type { PermissionContext } from '../../../types';
import type { InitializedClient } from '../../types';

export type EncodeRedeemDelegationsParameters = {
delegations: Delegation[][];
delegations: PermissionContext[];
modes: ExecutionMode[];
executions: ExecutionStruct[][];
};
Expand All @@ -37,7 +37,7 @@ export const simulate = async ({
abi: DelegationManager,
functionName: 'redeemDelegations',
args: [
encodePermissionContexts(delegations),
delegations.map((delegation) => encodeDelegations(delegation)),
modes,
encodeExecutionCalldatas(executions),
],
Expand Down Expand Up @@ -71,7 +71,7 @@ export const encode = ({
abi: DelegationManager,
functionName: 'redeemDelegations',
args: [
encodePermissionContexts(delegations),
delegations.map((delegation) => encodeDelegations(delegation)),
modes,
encodeExecutionCalldatas(executions),
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,19 @@ import type {
SmartAccount,
} from 'viem/account-abstraction';

import { encodeDelegations } from '../delegation';
import {
createExecution,
encodeExecutionCalldatas,
ExecutionMode,
} from '../executions';
import { getSmartAccountsEnvironment } from '../smartAccountsEnvironment';
import type { Call } from '../types';
import type { Call, PermissionContext } from '../types';

export type DelegatedCall = Call &
OneOf<{ permissionsContext: Hex; delegationManager: Hex } | object>;
OneOf<
{ permissionContext: PermissionContext; delegationManager: Hex } | object
>;

export type SendTransactionWithDelegationParameters<
TChain extends Chain | undefined = Chain | undefined,
Expand All @@ -35,7 +38,7 @@ export type SendTransactionWithDelegationParameters<
TRequest extends SendTransactionRequest<TChain, TChainOverride> =
SendTransactionRequest<TChain, TChainOverride>,
> = SendTransactionParameters<TChain, TAccount, TChainOverride, TRequest> & {
permissionsContext: Hex;
permissionContext: PermissionContext;
delegationManager: Hex;
};

Expand Down Expand Up @@ -71,15 +74,15 @@ export async function sendTransactionWithDelegationAction<
abi: DelegationManager,
functionName: 'redeemDelegations',
args: [
[args.permissionsContext],
[encodeDelegations(args.permissionContext)],
[ExecutionMode.SingleDefault],
encodeExecutionCalldatas([executions]),
],
});

const {
value: _value,
permissionsContext: _permissionsContext,
permissionContext: _permissionContext,
delegationManager: _delegationManager,
...rest
} = args;
Expand Down Expand Up @@ -127,7 +130,7 @@ export type SendUserOperationWithDelegationParameters<
* functionName: 'increment',
* }),
* value: 0n,
* permissionsContext: '0x...',
* permissionContext: '0x...',
* delegationManager: '0x...',
* },
* ],
Expand Down Expand Up @@ -206,6 +209,22 @@ export async function sendUserOperationWithDelegationAction<
];
}

parameters.calls = parameters.calls.map((call) => {
if (!('permissionContext' in call)) {
return call;
}

const { permissionContext } = call;
if (!permissionContext) {
return call;
}

return {
...call,
permissionContext: encodeDelegations(permissionContext),
};
});

return client.sendUserOperation(
parameters as unknown as SendUserOperationParameters,
);
Expand Down
17 changes: 0 additions & 17 deletions packages/smart-accounts-kit/src/caveats.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import {
type Hex,
encodePacked,
encodeAbiParameters,
parseAbiParameters,
keccak256,
Expand Down Expand Up @@ -33,22 +32,6 @@ export const getCaveatPacketHash = (input: Caveat): Hex => {
return keccak256(encoded);
};

/**
* Calculates the hash of an array of Caveats.
*
* @param input - The array of Caveats.
* @returns The keccak256 hash of the encoded Caveat array packet.
*/
export const getCaveatArrayPacketHash = (input: Caveat[]): Hex => {
let encoded: Hex = '0x';

for (const caveat of input) {
const caveatPacketHash = getCaveatPacketHash(caveat);
encoded = encodePacked(['bytes', 'bytes32'], [encoded, caveatPacketHash]);
}
return keccak256(encoded);
};

/**
* Creates a caveat.
*
Expand Down
Loading
Loading