0

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);
    }

}
1

1 Answer 1

5

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.

3
  • Hi Rob Hitchens, I tried to put the token.approve(address(Escrow), amount) in the same contract function before token.transferFrom. But the approve didn't work (no approve happened) (@openZeppelin's ERC20). I think the token.approve has to be done in dapp before calling escrow deposit in escrow contract.
    – user938363
    Commented Jan 3, 2021 at 6:24
  • 1
    That's the thing. The contract can't approve itself to transfer from. That would be like me writing a contract that approves the transfer of your money to ... guess who? It would be chaos. It goes User Signs => approve(that contract, this much), then the contract goes transferFrom(that user, this much). Of course, the user kicks off the second part, so for the user it's a) approve then b) spend. Commented Jan 3, 2021 at 6:37
  • You are right. I realize that the approve is done on UI in your post. Many thanks.
    – user938363
    Commented Jan 3, 2021 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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