Search code examples
ethereumsolidityerc20

Error calling deposit function on ERC20 escrow contract


I wrote this contract to escrow a given type of ERC20 token (address of the token is given when calling the constructor). But every time I call the deposit function to deposit some tokens into the contract, I just get transaction failed and I'm not sure why.


pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MainEscrow is Ownable {

    IERC20 public token;

    event Deposited(
        address indexed payee,
        address tokenAddress,
        uint256 amount
    );
    event Withdrawn(
        address indexed payee,
        address tokenAddress,
        uint256 amount
    );

    // payee address => token address => amount
    mapping(address => mapping(address => uint256)) public deposits;

    // payee address => token address => expiration time
    mapping(address => mapping(address => uint256)) public expirations;

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

    function deposit(
        address _payee,
        uint256 _amount,
        uint256 _expiration
    ) public {
        token.transferFrom(msg.sender, address(this), _amount);
        deposits[_payee][address(token)] += _amount;
        expirations[_payee][address(token)] = block.timestamp + _expiration;
        emit Deposited(_payee, address(token), _amount);
    }

    function withdraw(address payable _payee, uint256 _amount) public {
        uint256 totalPayment = deposits[_payee][address(token)];
        require(totalPayment >= _amount, "Not enough value");
        token.approve(_payee, _amount);
        require(token.transfer(_payee, _amount));
        deposits[_payee][address(token)] = totalPayment - _amount;
        emit Withdrawn(_payee, address(token), _amount);
    }

    function refund(address payable _payee) public {
        require(
            block.timestamp > expirations[_payee][address(token)],
            "The payment is still in escrow."
        );
        uint256 payment = deposits[_payee][address(token)];
        token.approve(msg.sender, payment);
        require(token.transfer(msg.sender, payment), "Transfer failed");
        deposits[_payee][address(token)] = 0;
        emit Withdrawn(msg.sender, address(token), payment);
    }
}

Solution

  • There's practically only one line that could fail in the deposit() function:

    token.transferFrom(msg.sender, address(this), _amount);
    

    (It could theoretically also revert because of an integer overflow on the following two lines, but this is very improbable.)

    This line executes the function tranferFrom() on the token contract. As the ERC-20 standard defines, the function should revert if the token sender doesn't have sufficient balance, or if they haven't approved the caller (in your case the MainEscrow address) to spend that much of their tokens.

    So either the token sender doesn't have sufficient balance, or they haven't called the approve(<MainEscrow_address>, <amount>) function from their address on the token contract.