How does transferFrom(), in ERC20.sol, enforce that the caller must have allowance for `sender`'s tokens of at least `amount`?

I’m brand new to Solidity and am wondering how, in openzeppelin-solidity 2.3.0, transferFrom(), in ERC20.sol, enforces that the caller must have allowance for sender's tokens of at least amount?

I see that in earlier versions of openzeppelin-solidity, transferFrom() does a require check for this but I don’t see this in 2.3.0. Can someone kindly explain this to me? Wondering if I need to implement this myself? Thank you.

2 Likes

Hi @Steve_L
Welcome to the community and Solidity.

The enforcement still exists but is more gas efficient. The improvement was made in OpenZeppelin 2.1.1

https://github.com/OpenZeppelin/openzeppelin-solidity/releases/tag/v2.1.1

  • ERC20 and ERC721 are now more gas efficient due to removed redundant SSTORE s and require s. (#1409 and #1549)

The allowance check is handled by the subtraction from _allowances using SafeMath.
If the allowance is not sufficient then there is a revert with reason "SafeMath: subtraction overflow"

ERC20.sol

The check is performed in SafeMath sub function.

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

SafeMath.sol

    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        require(b <= a, "SafeMath: subtraction overflow");
        uint256 c = a - b;

        return c;
    }

ERC20.test.js

An example of one of the tests

          it('reverts when more than the full allowance is removed', async function () {
            await expectRevert(
              this.token.decreaseAllowance(spender, approvedAmount.addn(1), { from: initialHolder }),
              'SafeMath: subtraction overflow'
            );
          });

For information on how require works you can read the Solidity documentation:
https://solidity.readthedocs.io/en/latest/control-structures.html#error-handling-assert-require-revert-and-exceptions

For more detail on the Pull Request which removed the redundant require statements:


Please ask all the questions that you need. :smile:

When you have a moment it would be awesome if you could introduce yourself to the community.

1 Like

Thanks very much @abcoathup! Super helpful. A bit of a newb question but…

in SafeMath.sol, the sub function takes two parameters (a and b), however, when sub() is called in ERC20.sol, it only passes in one single parameter (amount). I see that it is:

_allowances[sender][msg.sender].sub(amount));

Does using the dot notation (chaining) somehow pass _allowances in as the first parameter and “amount” as the second parameter to the sub method?

1 Like

Hi @Steve_L

All questions are good :smile:. (I had to look up the answer as I didn't know either).

_allowances is passed as the first parameter to sub due to the using for in the ERC20 contract.

contract ERC20 is IERC20 {
    using SafeMath for uint256;

The Solidity documentation explains how this works:

https://solidity.readthedocs.io/en/latest/contracts.html#using-for
The directive using A for B; can be used to attach library functions (from the library A ) to any type ( B ). These functions will receive the object they are called on as their first parameter (like the self variable in Python).

Housekeeping: I marked my previous reply as the solution to your question and moved to #support:openzeppelin category.

I would suggest asking a question per topic (unless the questions are related), so that other people can easily find the answers.

Thanks for asking questions in the forum :rocket:

1 Like

Thank you @abcoathup, that’s what I was guessing :smiley:

I’ll make sure to ask 1 question per topic in the future.

1 Like

Especially when you are asking such great questions that would be helpful to the rest of the community.

1 Like