[Ethereum] ERC20 – Approve & TransferFrom not working

contract-designerc-20remixtokenstransactions

The getTokenBalance function seems to retrieve data from the ERC20 contract just fine so I don't think there is an issue with how I'm using the interface. Additionally, I also tested out separating the Approve and TransferFrom calls into two separate functions. The approve function worked, however, the TransferFrom function did not.

The Error that is given is "Gas estimation errored with the following message (see below). The transaction execution will likely fail."

pragma solidity >=0.4.22 <0.6.0;

contract ERC20 {
    function totalSupply() public view returns (uint);
    function balanceOf(address tokenOwner) public view returns (uint balance);
    function allowance(address tokenOwner, address spender) public view returns (uint remaining);
    function transfer(address to, uint tokens) public returns (bool success);
    function approve(address spender, uint tokens) public returns (bool success);
    function transferFrom(address from, address to, uint tokens) public returns (bool success);
    event Transfer(address indexed from, address indexed to, uint tokens);
    event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}

contract Escrow {

    address token;

    constructor(address tokenAddress) public {
        token = tokenAddress;
    }

    function getTokenBalance(address user) public view returns (uint) {
        return ERC20(token).balanceOf(user);
    }

    function deposit(uint amnt) public {
        // transfer the tokens from the sender to this contract
        ERC20(token).approve(address(this), amnt);
        ERC20(token).transferFrom(msg.sender, address(this), amnt);
    }

}

Best Answer

You misunderstood the flow.

You have

User => Escrow.deposit() 
  1. Escrow => token.approve(this, amount)
  2. Escrow => token.transferFrom(msg.sender, amount); // msg.sender did not approve this

That won't work for the same reason I (for example) can't approve myself to withdraw your funds and then do it.

It has to go:

User => token.approve(address(Escrow), amount) // msg.sender approves Escrow to take some funds
User => Escrow.deposit()
 1. token.transferFrom(msg.sender, address(this), amount) // Escrow collects the approved amount
 2. Carry on

That way, the user approves for their own funds to be released to Escrow when Escrow comes to collect.

This two-stepper can be coordinated in a UI. Generally, the user will see two confirmation dialogue because they are sending two transactions. If something goes wrong with that process, then the UI should help the user through the process of revoking the approval that was granted. Rarely needed, but important. It's another transaction to set the approved amount to zero.

You may want to look into ERC223 for a possible alternative approach.

Hope it helps.

Related Topic