Search code examples
soliditysmartcontractstruffle

Error when transfering ERC20 from account to contract


I'm getting the following error when trying to transfer an IERC20 token from an account to a contract:
Error: Returned error: VM Exception while processing transaction: revert ds-math-sub-underflow -- Reason given: ds-math-sub-underflow. (it fails at the pair.transferFrom line).

But if I try to transfer from account to account it works, it only fails when I try to transfer from an account to the contract:

const PAIR = "0x397ff1542f962076d0bfe58ea045ffa2d347aca0";
const PAIR_WHALE = "0x7ac049b7d78bc930e463709ec5e77855a5dca4c4";

const pair = await IERC20.at(PAIR);
const myContract = await MyContract.deployed();
// const addr = accounts[0]; // Works!!!
const addr = myContract.address; // Fails with error above

let whaleBal = await pair.balanceOf(PAIR_WHALE);
await pair.approve(addr, whaleBal, { from: PAIR_WHALE });
await pair.transferFrom(PAIR_WHALE, addr, whaleBal);

My setup for running this:

  • mainnet fork with infura + ganache
  • PAIR is the address from the USDC-WETH pair from sushiswap
  • PAIR_WHALE unlocked via ganache
  • The snippet of code above is from a test file I run with truffle test

Solution

  • pair.approve(addr, whaleBal, { from: PAIR_WHALE });
    

    On this line, the PAIR_WHALE (transaction sender) approves the contract address (the value of addr) to spend their tokens.

    pair.transferFrom(PAIR_WHALE, addr, whaleBal)
    

    But on this line, the accounts[0] (the default transaction sender) is trying to spend the PAIR_WHALE's tokens. But the accounts[0] is not approved to do this.

    When you assigned the value of accounts[0] to addr in the commented line, the PAIR_WHALE effectively approved the accounts[0] to spend their tokens, which caused the transferFrom() function to pass.


    You cannot send a transaction on behalf of the contract address, as its private key is unknown.

    But if you want to transfer the PAIR_WHALE tokens to the contract address, you can simply invoke the transfer() function from the PAIR_WHALE address.

    // transfers `whaleBal` of `pair` tokens
    // from the `PAIR_WHALE` address
    // to the `myContract.address` address
    pair.transfer(myContract.address, whaleBal, {from: PAIR_WHALE});