5

For unit testing purposes, I want to impersonate the DAI contract to transfer some DAI to a local wallet address in my local blockchain environment. I have followed the instructions on Hardhat's mainnet forking guide, but I'm not really sure what the following does:

const signer = await ethers.provider.getSigner("0x364d6D0333432C3Ac016Ca832fb8594A8cE43Ca6")
signer.sendTransaction(...) // what does this do?

What are the parameters within signer.sendTransaction(), and is it only for sending Ether from one contract to another address? Can I make the impersonated DAI contract approve and transfer ETH from the DAI contract to a local wallet?

The purpose of this is for a local signer to be able to spend DAI to buy a special token minted by a ERC1155 contract I'm deploying. The only currency is DAI, and it is essential to increase the signer's balance for DAI to purchase that token.

2 Answers 2

6

What are the parameters within signer.sendTransaction()

Signer is an ethers.js concept. Check its docs to see how it is used, but a simple example is this:

await signer.sendTransaction({
  to: someAddress,
  value: ethers.utils.parseEther("1") // 1 ether
})

Can I make the impersonated DAI contract approve and transfer ETH from the DAI contract to a local wallet?

Yes, you can. What you can't do is impersonate the DAI contract to somehow modify the DAI balance of some address. If you want to get DAI, you need to impersonate an account with a lot of it, or just use the ETH in the unlocked accounts (Hardhat initializes some accounts with 1000 ETH each by default) and then get DAI as you'd do it in mainnet, like using an exchange or opening a Maker vault.

2
  • Could you use hardhat's helpers.setStorageAt to set the DAI balance of an address? I assume you could, but you'd have to deduce from the storage layout which slot the balance is in, which seems like a pain...
    – mbrig
    Commented Nov 22, 2022 at 3:29
  • Yes, that's exactly right. Commented Nov 22, 2022 at 7:23
0

More as addition to the comment (don't have enough reputation to comment)

You can manipulate the DAI balance easy with setStorageAt

Example Code:

const DAI_ADDRESS = "0x6b175474e89094c44da98b954eedeac495271d0f";
const DAI_SLOT = 2;

async() => {
    const Dai = new ethers.Contract(DAI_ADDRESS, erc20Abi, ethers.provider);
    const locallyManipulatedBalance = parseUnits("100000");

    const [user] = await ethers.getSigners();
    const userAddress = await user.getAddress();

    // Get storage slot index
    const index = ethers.utils.solidityKeccak256(
      ["uint256", "uint256"],
      [userAddress, DAI_SLOT] // key, slot
    );

    // Manipulate local balance (needs to be bytes32 string)
    await setStorageAt(
      DAI_ADDRESS,
      index.toString(),
      toBytes32(locallyManipulatedBalance).toString()
    );
}()

sources: https://kndrck.co/posts/local_erc20_bal_mani_w_hh/ (here is also a tool linked slot20 with which you can read out the storage slot for the balance of an erc20 token)

Not the answer you're looking for? Browse other questions tagged or ask your own question.