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

addLiquidity() safeApprove() the second execution may revert #1782

Closed
code423n4 opened this issue Sep 5, 2023 · 11 comments
Closed

addLiquidity() safeApprove() the second execution may revert #1782

code423n4 opened this issue Sep 5, 2023 · 11 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working primary issue Highest quality submission among a set of duplicates sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards

Comments

@code423n4
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2023-08-dopex/blob/eb4d4a201b3a75dd4bddc74a34e9c42c71d0d12f/contracts/amo/UniV2LiquidityAmo.sol#L200

Vulnerability details

Impact

In UniV2LiquidityAMO.addLiquidity() use IERC20WithBurn(addresses.token).safeApprove()
The safeApprove() method must satisfy that all previous allowances must be used up or it will revert
But in IUniswapV2Router.addLiquidity(), not all of the tokenA/tokenB allowances will be used up.
This causes the second execution of addLiquidity() to revert when safeApprove() doesn't use up all the allowances.

Proof of Concept

safeApprove() It must be ensured that previous allowances must be used up

    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
@>          (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

but addLiquidity() It doesn't use up the entire allowance.

https://github.com/Uniswap/v2-periphery/blob/0335e8f7e1bd1e8d8329fd300aea2ef2f36dd19f/contracts/UniswapV2Router02.sol#L71

    function addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin,
        address to,
        uint deadline
    ) external virtual override ensure(deadline) returns (uint amountA, uint amountB, uint liquidity) {
@>      (amountA, amountB) = _addLiquidity(tokenA, tokenB, amountADesired, amountBDesired, amountAMin, amountBMin);
        address pair = UniswapV2Library.pairFor(factory, tokenA, tokenB);
        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
        liquidity = IUniswapV2Pair(pair).mint(to);
    }

So on the second execution, UniV2LiquidityAMO.addLiquidity() will revert

test POC

The following code demonstrates that the second time addLiquidity() is revert with SafeERC20: approve from non-zero to non-zero allowance

add to Periphery.t.sol

  function testSafeApproveRevert() public {
    uniV2LiquidityAMO = new UniV2LiquidityAMO();

    // set addresses
    uniV2LiquidityAMO.setAddresses(
      address(rdpx),
      address(weth),
      address(pair),
      address(rdpxV2Core),
      address(rdpxPriceOracle),
      address(factory),
      address(router)
    );

    // give amo premission to access rdpxV2Core reserve tokens

    rdpxV2Core.addAMOAddress(address(uniV2LiquidityAMO));

    rdpxV2Core.approveContractToSpend(
      address(rdpx),
      address(uniV2LiquidityAMO),
      type(uint256).max
    );

    rdpxV2Core.approveContractToSpend(
      address(weth),
      address(uniV2LiquidityAMO),
      type(uint256).max
    );

    rdpx.transfer(address(rdpxV2Core), 50e18);
    weth.transfer(address(rdpxV2Core), 11e18);

    uniV2LiquidityAMO.addLiquidity(5e18, 2e18, 0, 0);
    uniV2LiquidityAMO.addLiquidity(5e18, 2e18, 0, 0);
  }  
$ forge test --match-test testSafeApproveRevert

Running 1 test for tests/rdpxV2-core/Periphery.t.sol:Periphery
[FAIL. Reason: SafeERC20: approve from non-zero to non-zero allowance] testSafeApproveRevert() (gas: 2324150)
Test result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 1.98s
Ran 1 test suites: 0 tests passed, 1 failed, 0 skipped (1 total tests)

Failing tests:
Encountered 1 failing test in tests/rdpxV2-core/Periphery.t.sol:Periphery
[FAIL. Reason: SafeERC20: approve from non-zero to non-zero allowance] testSafeApproveRevert() (gas: 2324150)

Encountered a total of 1 failing tests, 0 tests succeeded

Tools Used

Recommended Mitigation Steps

use safeIncreaseAllowance() instead of safeApprove()

Assessed type

ERC20

@code423n4 code423n4 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Sep 5, 2023
code423n4 added a commit that referenced this issue Sep 5, 2023
@c4-pre-sort
Copy link

bytes032 marked the issue as duplicate of #928

@c4-pre-sort
Copy link

bytes032 marked the issue as low quality report

@c4-pre-sort c4-pre-sort added the low quality report This report is of especially low quality label Sep 11, 2023
@c4-pre-sort
Copy link

bytes032 marked the issue as sufficient quality report

@c4-pre-sort c4-pre-sort added sufficient quality report This report is of sufficient quality and removed low quality report This report is of especially low quality labels Sep 11, 2023
@c4-pre-sort
Copy link

bytes032 marked the issue as not a duplicate

@c4-pre-sort
Copy link

bytes032 marked the issue as duplicate of #1455

@c4-pre-sort
Copy link

bytes032 marked the issue as not a duplicate

@c4-pre-sort c4-pre-sort added the primary issue Highest quality submission among a set of duplicates label Sep 11, 2023
@c4-pre-sort
Copy link

bytes032 marked the issue as primary issue

@bytes032
Copy link

This issue and its duplicates contain all the occurrences of:

https://github.com/code-423n4/2023-08-dopex-bot-findings/blob/main/data/IllIllI-bot-report.md#l05-approvesafeapprove-may-revert-if-the-current-approval-is-not-zero

Note: It's reported as L from the bot.

@c4-sponsor
Copy link

psytama (sponsor) confirmed

@c4-sponsor c4-sponsor added the sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") label Sep 25, 2023
@GalloDaSballo
Copy link

The finding is OOS

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Oct 12, 2023
@c4-judge
Copy link
Contributor

GalloDaSballo marked the issue as unsatisfactory:
Out of scope

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working primary issue Highest quality submission among a set of duplicates sponsor confirmed Sponsor agrees this is a problem and intends to fix it (OK to use w/ "disagree with severity") sufficient quality report This report is of sufficient quality unsatisfactory does not satisfy C4 submission criteria; not eligible for awards
Projects
None yet
Development

No branches or pull requests

6 participants