I’m building my dapp with scaffoldETH 2 and anvil. I want to show the features of eip-7702 and i’ve chosen to use the MetaMask delegation-toolkit. I have 3 smart account (Alice, Bob and Charles) and Alice must send ETH in batch to Bob and Charles. Alice became a smart account and has the smart contract code within that allows to send ETH in batch. So Alice signs a delegation to permit one sponsor (a normal EOA) to pay the gas on her behalf.
I think the problem is when the sponsor redeems the delegation, i get an error that says:
TransactionExecutionError: Execution reverted for an unknown reason.
83 | try {
> 84 | const txHash = await sponsorWalletClient.sendTransaction({
| ^
85 | to: getDeleGatorEnvironment(chainId).DelegationManager,
86 | data: redeemDelegationCalldata,
87 | });
here’s my code for the delegation:
export async function createDelegationForAccount(smartAccount: any,) {
const delegatorEnvironment = smartAccount.environment;
const caveatBuilder = createCaveatBuilder(delegatorEnvironment);
const caveats = createCaveatBuilder(environment)
.addCaveat("allowedMethods", ["execute((address,uint256,bytes)[])","multiSendETH(address[],uint256[])"])
.build();
const delegation = createDelegation({
to: sponsorAccount.address as `0x${string}`,
from: smartAccount.address as `0x${string}`,
caveats
});
const signature = await smartAccount.signDelegation({ delegation });
return { ...delegation, signature };
}
This is the redeem function:
export async function redeemDeleg(
signedDelegation: Delegation,
recipients: string[],
amounts: string[],
smartAccount: any,
) {
const { delegator, delegate, signature } = signedDelegation;
console.log("Delegator:", delegator);
console.log("Delegate:", delegate);
console.log("Caveats:", signedDelegation.caveats);
console.log("Delegation:", signedDelegation);
const mode: ExecutionMode = SINGLE_DEFAULT_MODE;
const chainId = 31337;
const contract = deployedContracts[chainId].MyContract;
const parsedAmounts = amounts.map(a => BigInt(Math.floor(parseFloat(a) * 1e18)));
const totalAmount = parsedAmounts.reduce((acc, x) => acc + x, 0n);
console.log("total amount:", totalAmount);
const multiSendData = encodeFunctionData({
abi: contract.abi,
functionName: "multiSendETH",
args: [recipients, parsedAmounts],
});
const execution: ExecutionStruct = {
target: smartAccount.address,
value: totalAmount,
callData: multiSendData
};
const delegations: Delegation[] = [signedDelegation];
const executions: ExecutionStruct[][] = [[execution]];
const redeemDelegationCalldata = DelegationManager.encode.redeemDelegations({
delegations: [delegations],
modes: [mode],
executions,
});
inspectRedeemCalldata(redeemDelegationCalldata);
try {
const txHash = await sponsorWalletClient.sendTransaction({
to: getDeleGatorEnvironment(chainId).DelegationManager,
data: redeemDelegationCalldata,
});
console.log("Transaction hash:", txHash);
} catch (err: any) {
console.error("Full error:", err);
if (err.cause?.data) {
console.error("Revert data:", err.cause.data);
}
}
}
Here’s also the contract code, i tested the contract before doing the delegation and it works fine.
contract MyContract {
struct Call {
address to;
uint256 value;
bytes data;
}
/// @notice Executes a batch of calls on behalf of the account
/// @dev Compatible with Delegation Toolkit caveat: allowedMethods = ["execute((address,uint256,bytes)[])"]
function execute(Call[] calldata calls) external payable {
for (uint256 i = 0; i < calls.length; i++) {
(bool success, ) = calls[i].to.call{value: calls[i].value}(calls[i].data);
require(success, "Call failed");
}
}
/// @notice Sends ETH to multiple recipients
function multiSendETH(address[] calldata recipients, uint256[] calldata amounts) external payable {
require(recipients.length == amounts.length, "Length mismatch");
uint256 total = 0;
for (uint256 i = 0; i < amounts.length; i++) {
total += amounts[i];
}
require(msg.value >= total, "Insufficient ETH");
for (uint256 i = 0; i < recipients.length; i++) {
(bool sent, ) = recipients[i].call{value: amounts[i]}("");
require(sent, "ETH send failed");
}
}
/// @notice Returns the contract address
function getAddress() external view returns (address) {
return address(this);
}
/// @notice Accepts ETH
receive() external payable {}
}
I can’t figure out what is the problem, i tested so many solution but none resolve the error.
I need an help with the problem because it’s blocking me from go ahead for almost a week now.
Thanks to everyone.