3
votes

While attempting to send tokens via a contract I run in to the error message: "SafeMath: subtraction overflow."

Initially I only used the transfer functionality. However, as I thought the msg.sender only has to send its tokens to the other user (via truffle console this is no issue). However, reading [this] I got the impression it is actually the contract address that becomes the msg.sender in the TokenContract. Therefore, (as only the accounts but not the contract itself) I thought that I have to send tokens to the contract first, subsequently approve that the contract is allowed to send tokens on behalf of the msg.sender and subsequently transfer the money. However, I keep having the SafeMath error.

The TokenContract (not the interface below) implements the The most simple version of my code is as follows:

contract ContractA {

    function pay () public returns (bool) {

        TokenContract tk = TokenContract("tokenContractAddress");
        tk.transferFrom(msg.sender, address(this), 5);
        tk.approve(address(this), 5);
        tk.transfer("someAccount", 5);

        return true;
    }
}

interface TokenContract {
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool);
    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address recipient, uint256 amount) external returns (bool);   
}

contract TokenContract is ERC20, ERC20Detailed {
    constructor() ERC20Detailed("Token", "TKN", 18) public {
        _mint(msg.sender, 1000);
    }
}

Obviously I expect not the safeMath error to appear. As I transfer money and approve. I just expect the same behaviour as when using truffle console.

1
You don't need tk.approve(address(this), 5). You do need to make sure you approve ContractA before calling pay().user94559
Thank you so much @smarx. I did not have the time to play around earlier, but you clarified it. Got it working!Crittje

1 Answers

4
votes

I assume that the address calling ContractA hasn't set an allowance for the contract.

Requirements:
* the caller must have allowance for sender's tokens of at least amount. https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v2.3.0/contracts/token/ERC20/ERC20.sol

    /**
     * @dev See `IERC20.transferFrom`.
     *
     * Emits an `Approval` event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of `ERC20`;
     *
     * Requirements:
     * - `sender` and `recipient` cannot be the zero address.
     * - `sender` must have a balance of at least `value`.
     * - the caller must have allowance for `sender`'s tokens of at least
     * `amount`.
     */
    function transferFrom(address sender, address recipient, uint256 amount) public returns (bool) {
        _transfer(sender, recipient, amount);
        _approve(sender, msg.sender, _allowances[sender][msg.sender].sub(amount));
        return true;
    }
  1. Caller of Contract A approves an allowance for the Token
  2. Caller of Contract A calls pay function and which calls transferFrom to transfer from the user to the recipient within the previously set allowance.

If you are creating ERC20 tokens you may want to look at the OpenZeppelin Contracts implementation to see if this meets your needs. See the documentation for details: https://docs.openzeppelin.com/contracts/2.x/tokens#ERC20

If you are transferring a variety of ERC20 tokens you may want to consider using SafeERC20 wrapper to make these calls: https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#SafeERC20

If you need an interface for ERC20 you may want to look at using IERC20: https://docs.openzeppelin.com/contracts/2.x/api/token/erc20#IERC20

Alternatively to ERC20 you could look at creating ERC777 tokens (no need to do approve and transferFrom in two separate transactions). See the documentation for details: https://docs.openzeppelin.com/contracts/2.x/tokens#ERC777

If you have questions on using OpenZeppelin you can ask in the Community Forum: https://forum.openzeppelin.com/

Disclosure: I am the Community Manager at OpenZeppelin