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

Fix to ExampleCombinedSwapAddRemoveLiquidity.sol: using MAX_UINT approvals with the router #38

Conversation

olivdb
Copy link

@olivdb olivdb commented Jul 22, 2020

Some tokens like USDT do not allow approving an amount M > 0 when an existing amount N > 0 is already approved. This is to protect from an ERC20 attack vector described here https://docs.google.com/document/d/1YLPtQxZu1UAvO9cZ1O2RPXBbT0mooh4DYKjA_jp-RLM/edit#heading=h.b32yfk54vyg9.

ExampleCombinedSwapAddRemoveLiquidity approves UniswapV2Router to pull some tokens from itself but UniswapV2Router does not always use its full allowance and sometimes leaves some residual allowance after the transaction. As a result, the next time ExampleCombinedSwapAddRemoveLiquidity tries to approve UniswapV2Router, the residual allowance makes the approval fail for tokens such as USDT.

This PR resolves this issue by approving the router to transfer MAX_UINT tokens from ExampleCombinedSwapAddRemoveLiquidity.

…that don't support changing a non-zero approval
// grants unlimited approval for a token to the router unless the existing allowance is high enough
function approveRouter(address _token, uint256 _amount) internal {
if (IERC20(_token).allowance(address(this), address(router)) < _amount) {
TransferHelper.safeApprove(_token, address(router), uint256(-1));
Copy link
Contributor

Choose a reason for hiding this comment

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

don't we have to approve(0) first?

Copy link
Author

Choose a reason for hiding this comment

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

Yes, indeed. I had assumed that tokens that forbid calling approve() with a non-zero amount when the existing allowance is non-zero would skip the allowance update in transferFrom() when the allowance is MAX_UINT (as is the case for USDT) but thinking about this more, I see no reason to make this assumption. Will fix.

…owance has shrank to less than the required amount
@olivdb olivdb requested a review from moodysalem July 22, 2020 17:52
@@ -53,7 +53,7 @@ describe('ExampleCombinedSwapAddRemoveLiquidity', () => {
await wethPair.mint(wallet.address, overrides)
}

beforeEach(async function() {
beforeEach(async function () {
Copy link
Contributor

Choose a reason for hiding this comment

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

prob causing a linter issue

@moodysalem
Copy link
Contributor

code change lgtm, i don't see any way this could cause a security issue, could use a test but this is use-at-your-own-risk code, happy to merge this once you fix the linter error so the tests pass

@moodysalem moodysalem merged commit e8919d8 into Uniswap:swap-before-liquidity-events Jul 23, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants