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

DoS when add liquidity #35

Closed
code423n4 opened this issue Aug 23, 2023 · 7 comments
Closed

DoS when add liquidity #35

code423n4 opened this issue Aug 23, 2023 · 7 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 duplicate-1782 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/main/contracts/amo/UniV2LiquidityAmo.sol#L200-L204

Vulnerability details

Impact

Currently, in addLiquidity function of UniV2LiquidityAMO contract, it will first approve tokenA and tokenB to corresponding AMMRouter then add liquidity. But as we know, some ERC20 token has a trick in its approve function, it don't allow user to approve again when there is already an allowance. As we can see in the implementation of UniswapV2Router contract, the addLiquidity function does not always transfer all tokens specify in amountADesired and amountADesired. Instead, it will only transfer tokenA amount which calculated by amountBOptimal or tokenB amount which is calculated by amountAOptimal. So in most of cases, the allowance of tokenA and tokenB will not be fully used, whcih leads to user may not able to add liquidity because some “tricks” in ERC20 tokens

Proof of Concept

UniswapV2Router related functions

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);
        //only transfer tokens amount calculated by _addLiquidity function
        TransferHelper.safeTransferFrom(tokenA, msg.sender, pair, amountA);
        TransferHelper.safeTransferFrom(tokenB, msg.sender, pair, amountB);
        liquidity = IUniswapV2Pair(pair).mint(to);
    }

    function _addLiquidity(
        address tokenA,
        address tokenB,
        uint amountADesired,
        uint amountBDesired,
        uint amountAMin,
        uint amountBMin
    ) internal virtual returns (uint amountA, uint amountB) {
        // create the pair if it doesn't exist yet
        if (IUniswapV2Factory(factory).getPair(tokenA, tokenB) == address(0)) {
            IUniswapV2Factory(factory).createPair(tokenA, tokenB);
        }
        (uint reserveA, uint reserveB) = UniswapV2Library.getReserves(factory, tokenA, tokenB);
        if (reserveA == 0 && reserveB == 0) {
            (amountA, amountB) = (amountADesired, amountBDesired);
        } else {
            //calculate tokenA and tokenB amount again, which not directly use `amountADesired` and `amountBDesired`
            uint amountBOptimal = UniswapV2Library.quote(amountADesired, reserveA, reserveB);
            if (amountBOptimal <= amountBDesired) {
                require(amountBOptimal >= amountBMin, 'UniswapV2Router: INSUFFICIENT_B_AMOUNT');
                (amountA, amountB) = (amountADesired, amountBOptimal);
            } else {
                uint amountAOptimal = UniswapV2Library.quote(amountBDesired, reserveB, reserveA);
                assert(amountAOptimal <= amountADesired);
                require(amountAOptimal >= amountAMin, 'UniswapV2Router: INSUFFICIENT_A_AMOUNT');
                (amountA, amountB) = (amountAOptimal, amountBDesired);
            }
        }
    }

dopex related

function addLiquidity(
    uint256 tokenAAmount,
    uint256 tokenBAmount,
    uint256 tokenAAmountMin,
    uint256 tokenBAmountMin
  )
    external
    onlyRole(DEFAULT_ADMIN_ROLE)
    returns (uint256 tokenAUsed, uint256 tokenBUsed, uint256 lpReceived)
  {
    // approve the AMM Router
    // may get stuck here because some ERC20 tokens don't allow to approve again when there is already an unused allowance
    IERC20WithBurn(addresses.tokenA).safeApprove(
      addresses.ammRouter,
      tokenAAmount
    );
    IERC20WithBurn(addresses.tokenB).safeApprove(
      addresses.ammRouter,
      tokenBAmount
    );
    //........
}

Tools Used

VS code

Recommended Mitigation Steps

Approve allowance to 0 first before approve again

Assessed type

DoS

@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 Aug 23, 2023
code423n4 added a commit that referenced this issue Aug 23, 2023
@c4-pre-sort
Copy link

bytes032 marked the issue as duplicate of #928

@c4-pre-sort c4-pre-sort added duplicate-928 low quality report This report is of especially low quality labels Sep 8, 2023
@c4-pre-sort
Copy link

bytes032 marked the issue as low quality report

@c4-pre-sort c4-pre-sort reopened this 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 duplicate of #1782

@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 sufficient quality report

@c4-judge
Copy link
Contributor

GalloDaSballo marked the issue as unsatisfactory:
Out of scope

@c4-judge c4-judge added the unsatisfactory does not satisfy C4 submission criteria; not eligible for awards label Oct 12, 2023
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 duplicate-1782 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

3 participants