Skip to content

Impersonating account on forked mode submitting transaction reverts with RejectCallerWithCode #2931

@sunwrobert

Description

@sunwrobert

Component

Anvil

Have you ensured that all of these are up to date?

  • Foundry
  • Foundryup

What version of Foundry are you on?

0.1.0 (5910e7b 2022-08-25T00:39:34.671729Z)

What command(s) is the bug in?

anvil

Operating System

No response

Describe the bug

Hey forge team, back at it again.

I think this is one of the last major bugs that we've noticed in our tests.

It seems that submitting a transaction while impersonating account doesn't work in forked mode. At least, when run with the same exact configuration on Hardhat, it works, but with Anvil, it rejects with RejectCallerWithCode

This is the anvil command I'm running:

anvil --fork-url=FORKING_URL --fork-block-number=15187944 --port=8545 --block-time=5 4000 4

And this is the hardhat config I'm comparing it to:

// hardhat.config.ts
import { HardhatUserConfig } from 'hardhat/config';
import '@nomicfoundation/hardhat-toolbox';

const config: HardhatUserConfig = {
  solidity: '0.8.9',
  networks: {
    hardhat: {
      chainId: 1,
      forking: {
        url: process.env.FORKING_URL,
        blockNumber: 15347924, // https://etherscan.io/block/15347924
      },
      mining: {
        interval: 5000,
      },
    },
  },
  paths: {
    cache: './cache/ethereum',
  },
};

export default config;

And this is repro-able code. All it does is try to impersonate a WETH holder and transfer some WETH to an account.

import { Contract, providers, utils, ethers } from 'ethers';

const TOKEN_CONFIGS = {
  WETH: {
    // https://etherscan.io/token/0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2?a=0x2f0b23f53734252bda2277357e97e1517d6b042a
    address: '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2',
    decimals: 18,
    tokenHolder: '0x2f0b23f53734252bda2277357e97e1517d6b042a',
  },
};

type SetBalanceParams = {
  symbol: 'WETH';
  amount: string;
  address: string;
  provider: providers.JsonRpcProvider;
};

const setBalance = async ({
  symbol,
  amount,
  address: tokenReceiver,
  provider,
}: SetBalanceParams) => {
  const tokenConfig = TOKEN_CONFIGS[symbol];

  const { address: contractAddress, decimals, tokenHolder } = tokenConfig;

  const contractAbi = [
    // Get the account balance
    'function balanceOf(address) view returns (uint)',

    // Send some of your tokens to someone else
    'function transfer(address to, uint amount)',
  ];
  const contract = new Contract(contractAddress, contractAbi, provider);

  // Fund token holder so they can make the transaction
  await provider.send('hardhat_setBalance', [
    tokenHolder,
    utils.parseEther('1.0').toHexString().replace('0x0', '0x'),
  ]);

  // Impersonate the token holder
  await provider.send('anvil_impersonateAccount', [tokenHolder]);

  // Get the token holder signer
  const signer = await provider.getSigner(tokenHolder);

  // Connect signed with the contract
  const contractWithSigner = contract.connect(signer);

  // Tranfer funds
  const unitAmount = utils.parseUnits(amount, decimals);
  await contractWithSigner.transfer(tokenReceiver, unitAmount);

  await provider.send('anvil_stopImpersonatingAccount', [tokenHolder]);
};

async function main() {
  const provider = new ethers.providers.JsonRpcProvider();

  await setBalance({
    symbol: 'WETH',
    amount: '1',
    address: '0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266',
    provider,
  });
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});

To repro:

  1. Launch anvil in backgronud
  2. Start a new hardhat project with scripts/test.ts as the TS code I pasted above
  3. Run npx hardhat scripts/test.ts

These are the logs I get on Anvil:

anvil_setBalance
eth_chainId
anvil_impersonateAccount
eth_blockNumber
eth_chainId
eth_chainId
eth_estimateGas

    Block Number: 15187956
    Block Hash: 0x4477b3a21f37cb25e149c747c95c8f74e00621b0f8d97f27df88f25a0fd5391d
    Block Time: "Thu, 21 Jul 2022 19:50:49 +0000"

eth_sendTransaction
eth_chainId
eth_getTransactionByHash
anvil_stopImpersonatingAccount

    Transaction: 0x26c447de2b29a123314dfc81f4c7e1bd57b1cb5915b5f7deaceb6761481221f9
    Gas used: 0
    Error: failed due to RejectCallerWithCode

    Block Number: 15187957
    Block Hash: 0x1d0e669412bb9d26801eb1e2ab7ad6d535eb238fa656f67a9ef9a7a4a59f7763
    Block Time: "Thu, 21 Jul 2022 19:50:54 +0000"

And these are the logs I get on Hardhat:

Mined empty block range #15347925 to #15347928
hardhat_setBalance
eth_chainId
hardhat_impersonateAccount
eth_blockNumber
eth_chainId (2)
eth_estimateGas
eth_sendTransaction
eth_chainId
eth_getTransactionByHash
hardhat_stopImpersonatingAccount
Mined block #15347929
  Block: 0x9d04a732b5d7b80d9f90e0b7d53e063b87d0612dd3f596a2d43ee95d97211954
    Base fee: 8453171509
    Transaction:     0x660e4d8fa6d070d3f6ad42ae943b73b86cb236f9290ade9e5d00caa600c0ead2
      Contract call: <UnrecognizedContract>
      From:          0x2f0b23f53734252bda2277357e97e1517d6b042a
      To:            0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
      Value:         0 ETH
      Gas used:      51594 of 51594

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

Done

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions