Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Do not reduce approval on transferFrom if current allowance is type(uint256).max #3085

Merged
merged 13 commits into from
Jan 10, 2022

Conversation

0xclaudeshannon
Copy link
Contributor

@0xclaudeshannon 0xclaudeshannon commented Jan 6, 2022

Fixes #3084

The Wrapped Ether (WETH) ERC-20 contract has a gas optimization that does not update the allowance if it is the max uint. I'd like to incorporate this into OpenZeppelin to save 5000 gas per transferFrom call when a max approve is used.

The transferFrom code for WETH is:

    function transferFrom(address src, address dst, uint wad)
        public
        returns (bool)
    {
        require(balanceOf[src] >= wad);

        if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {
            require(allowance[src][msg.sender] >= wad);
            allowance[src][msg.sender] -= wad;
        }

        balanceOf[src] -= wad;
        balanceOf[dst] += wad;

        Transfer(src, dst, wad);

        return true;
    }

The key line is if (src != msg.sender && allowance[src][msg.sender] != uint(-1)) {. If the allowance is the max uint (0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff), then the WETH contract does not update the allowance mapping. This saves 5000 gas for the SSTORE. The only downside is that this changes the abstraction for approvals; if you set the approval to the maximum amount, then the contract can spend more than the max uint of your token. However, this doesn't seem like a risk, as users wanting to actually specify that amount can simply do the max uint minus 1.

My proposed change is to incorporate this into OpenZeppelin. This could save 5000 gas (or 10-20%) per ERC-20 transferFrom, which is used all across Unsiwap, Compound, and other big protocols.

I need feedback from the team for how to add this to the documentation. It passes all the tests.

The idea for this PR came up in the GAS DAO community discord.

PR Checklist

  • Tests
  • Documentation
  • Changelog entry

@earlit
Copy link

earlit commented Jan 6, 2022

This could save users Millions on Gas

@Amxx Amxx self-assigned this Jan 7, 2022
@Amxx Amxx added the feature New contracts, functions, or helpers. label Jan 7, 2022
@Amxx
Copy link
Collaborator

Amxx commented Jan 7, 2022

Hello @0xclaudeshannon and thank you for submitting this PR

This was already identified when looking at possible gas optimizations.

AFAIK, ERC20 is not really strict about the approval mechanism. There are changes that technically be breaking, but that would still match the ERC requirement:

  • not emitting an Approval event when a transferFrom consumes some/all of the approval.
  • not updating the approval value if it's set to type(uint256).max

I'm uncomfortable with the first one, but the second one, that you are proposing, looks reasonable to me.

@Amxx Amxx changed the title add feature request #3084 Do not reduce approval on transferFrom if current allowance is type(uint256).max Jan 7, 2022
@frangio
Copy link
Contributor

frangio commented Jan 7, 2022

Yes I think this is reasonable mainly because this pattern is already presented to users as "infinite approval" which is literaly what this PR implements.

Co-authored-by: Francisco Giordano <frangio.1@gmail.com>
@Amxx Amxx mentioned this pull request Jan 7, 2022
@frangio
Copy link
Contributor

frangio commented Jan 7, 2022

Missing changelog entry and documentation before merging.

@0xclaudeshannon
Copy link
Contributor Author

Missing changelog entry and documentation before merging.

Changelog in b34920b

What should I add for documentation? And which file is that in

@0xclaudeshannon
Copy link
Contributor Author

Thanks for changing it to type(uint256).max that's much more aesthetically pleasing.

@frangio
Copy link
Contributor

frangio commented Jan 8, 2022

For documentation I think we should add a mention of the behavior in the NatSpec for transferFrom.

@0xclaudeshannon
Copy link
Contributor Author

For documentation I think we should add a mention of the behavior in the NatSpec for transferFrom.

This good? 04d88dd

@Amxx
Copy link
Collaborator

Amxx commented Jan 9, 2022

I think this documentation should go in ERC20.sol ... because it is a particularity of the implementation that is not part of the interface/ERC design.

Also, I'm always personally prefer using uint256 over uint, particularly when talking about the maximum value.

@Amxx
Copy link
Collaborator

Amxx commented Jan 9, 2022

  • added tests.
  • added equivalent mechanism to ERC777 transferFrom for consistency

@0xclaudeshannon
Copy link
Contributor Author

I think this documentation should go in ERC20.sol ... because it is a particularity of the implementation that is not part of the interface/ERC design.

Also, I'm always personally prefer using uint256 over uint, particularly when talking about the maximum value.

fixed

Copy link
Contributor

@frangio frangio left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature New contracts, functions, or helpers.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Use WETH gas optimization for ERC-20 transferFrom
4 participants