Error: Revert when using ERC20 transferFrom

I build a contract, where a user can deposit a custom token using the createStake function like that:

contract StakingRewards  {
    
    IERC20 token;

    //...

    constructor(address _tokenAddress){
       token = ERC20(_tokenAddress);
    } 

    //...

    function createStake(uint256 _stake) public {
        require(token.transferFrom(msg.sender, address(this), _stake)); // <-- This seems to be the problem with truffle test
        if(stakes[msg.sender] == 0) _addStakeHolder(msg.sender);
        stakes[msg.sender] = stakes[msg.sender].add(_stake);
    }

}

When using the console truffle console --network development everything work. as expected:

  • I first approve the token allowance for the staking rewards contract like that: token.approve(StakingRewards.address, "100000000000", {from: "0x232....."})
  • and then call createStake: createStake("100000000000", {from: "0x232...."}) and it’s working.

When i’m trying to test it, i’m having some issue, it keep reverting: Error: Returned error: VM Exception while processing transaction: revert
This is the code i’m using for the test:

   //... 

const customToken = artifacts.require("CustomToken");
const StakingRewards = artifacts.require("StakingRewards");

contract('StakingRewards', ([owner, staker1,]) => {
  beforeEach( async () => {
    customToken = await CustomToken.new("1000000000000000000000000");

     //...

    stakingRewards = await StakingRewards.new(CustomToken.address);
    await customToken.transfer(staker1, "5000000000000000000000");
  });

  it("Should create/add stake correctly ", async () => {
    const stakeAmount = "10000000000000000000";
    await customToken.approve(StakingRewards.address, stakeAmount, {from: staker1 });
    await stakingRewards.createStake(stakeAmount, { from: staker1 }); // <--- This keep failling, with "Error VM Exception while processing transaction: revert"
   
    //...
  });
}); 

Can i have some help ? Why this test keep failing ? What did i got wrong ?

Thanks

Hey, welcome!

I think your steps are right,

1. token.approve(staking.address, amount);
2. staking.createStake();

And yes, I also think the problem is at this line: require(token.transferFrom(msg.sender, address(this), _stake)); I think maybe your staking token is not a standard ERC20 token, for example, USDT, is not a standard ERC20 token, so it does not have a return value for the function transferFrom, so even though usdt.transferFrom() succeed, this require will fail, so I think you had better use a compatible way, such as using the library of OpenZeppelin: SafeERC20.sol, and it can be:

using SafeERC20 for IERC20; // or using SafeERC20 for ERC20;

function createStake(uint256 _stake) public {
    token.safeTransferFrom(msg.sender, address(this), _stake));
}
2 Likes

Hey, Thanks for your reply.
I tried but it still not working, i’m getting: Error: Returned error: VM Exception while processing transaction: revert SafeERC20: low-level call failed -- Reason given: SafeERC20: low-level call failed.

A little weird, so can you share me with a failed transaction?

I know this is old post, but I believe the issue is with your test file.... the beforeEach(async() will run prior to each test and it is not passing the default sender address so in the test perspective there is no msg.sender. This is why it works via console but tests fail.

In your test "describe" you can use the unlocked accounts that get generated during the test:
const [ contractDeployer ] = accounts; // contractDeployer == accounts[0]