Skip to content

feat(cheatcodes): Add cheatcode to generate delegation with a given nonce #10181

Closed
@jes16jupyter

Description

@jes16jupyter

Component

Forge

Describe the feature you would like

Proposal

Add cheatcodes signDelegationWithNonce and signAndAttachDelegationWithNonce to support delegation signing with a specified nonce.

Motivation

Currently, signDelegation and signAndAttachDelegation consume the current nonce of the EOA and increment it when attaching the delegation to a transaction.

This approach only works when the transaction initiator is not the same EOA that signs the delegation. For example, if Alice wants to delegate execution to a contract, she must rely on Bob to send the transaction under Foundry’s current implementation—she cannot send it herself.

This issue can be demonstrated with the following Forge script:

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import {Script, console} from "forge-std/Script.sol";
import {Vm} from "forge-std/Vm.sol";

contract Impl {
    uint public number;

    function test() public returns (uint){
        return number++;
    }
}

contract TestScript is Script {
    Impl impl;

    // anvil test
    address alice = 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266;
    uint alice_PK = 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80;

    address bob = 0x70997970C51812dc3A010C7d01b50e0d17dc79C8;
    uint bob_PK = 0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d;

    function setUp() public {
        vm.startBroadcast(alice_PK);
        impl = new Impl();
        vm.stopBroadcast();
    }

    function run() public {
        vm.startBroadcast(alice_PK);
        vm.signAndAttachDelegation(address(impl), alice_PK);
        Impl(alice).test();
        console.log("alice.number", Impl(alice).number());
    }
}

When run against Anvil, the script executes successfully in a local simulation but fails when broadcasted on-chain. This is due to an incorrect nonce ordering between the transaction signing and authorization list signing. Ideally, transaction[nonce] should be 0x1 and transaction[authorizationList][0][nonce] should be 0x2, but the current implementation swaps them.

   {
      "hash": "0xebe57d35b56870787bd1585d37f85bc45d721efa7300b1d71a540dee969dfcf0",
      "transactionType": "CALL",
      "contractName": null,
      "contractAddress": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
      "function": "test()",
      "arguments": [],
      "transaction": {
        "from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
        "to": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
        "gas": "0xf889",
        "value": "0x0",
        "input": "0xf8a8fd6d",
        "nonce": "0x2",
        "chainId": "0x7a69",
        "authorizationList": [
          {
            "chainId": "0x7a69",
            "address": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
            "nonce": "0x1",
            "yParity": "0x1",
            "r": "0xb479f2c7d70a98008f27bbf4fb5e67daa0764459ed4c3337cdd906e53ac8b427",
            "s": "0x34d77ad2562e633a90f2a3c9d84098b4b6e6a9e8ad04d8484fe7d8d205f41483"
          }
        ]
      },
      "additionalContracts": [],
      "isFixedGasLimit": false
    }

The root cause is due to:

  • vm.signDelegation and vm.signAndAttachDelegation does not support signing with a specified nonce.
  • The attachDelegation process always occurs before the actual transaction, incrementing the nonce prematurely.

To partially resolve this, we propose adding signDelegationWithNonce and signAndAttachDelegationWithNonce cheatcodes to allow delegation signing with an explicit nonce.

Spec

  • Add vm.signDelegationWithNonce cheatcode
  • Add vm.signAndAttachDelegationWithNonce cheatcode

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    Status

    Completed

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions