First Flight #18: T-Swap

First Flight #18
Beginner FriendlyDeFiFoundry
100 EXP
View results
Submission Details
Severity: high
Valid

`TSwapPool::sellPoolTokens` mistakenly calls the incorrect swap function, causing users to receive the incorrect amount of tokens

Summary

TSwapPool::sellPoolTokens mistakenly calls the incorrect swap function.

Vulnerability Details

The sellPoolTokens function is intended to allow users to easily sell pool tokens and receive WETH in exchange. In the poolTokenAmount parameter, users indicate how many pool token they intend to sell. However, the function mistakenly calls swapExactOutput instead of swapExactInput to perform the swap, and therein assignes the value of poolTokenAmount to function input argument outputAmount, effectively mixing up the input and output tokens / amounts.

Consider the following scenario:

  1. A user has 100 pool tokens, and wants to sell 5 by calling the sellPoolTokens function.

  2. Instead of the swapExactInput function, sellPoolTokens calls swapExactOutput.

  3. In swapExactOutput, poolTokenAmount is used as outputAmount while it is really the input amount.

  4. As a result, user will swap more output tokens than originally intended.

Apart from this, the user will be overtaxed due to a bug in getInputAmountBasedOnOutput() called by swapExactOutput.

For a proof of code, add this piece of code to TSwapPool.t.sol:

Proof of Code
function test_sellPoolTokensCallsTheIncorrectSwapFunction() public {
// setting up the pool by providing liquidity
uint256 initialLiquidity = 100e18;
vm.startPrank(liquidityProvider);
weth.approve(address(pool), initialLiquidity);
poolToken.approve(address(pool), initialLiquidity);
pool.deposit({
wethToDeposit: initialLiquidity,
minimumLiquidityTokensToMint: 0,
maximumPoolTokensToDeposit: initialLiquidity,
deadline: uint64(block.timestamp)
});
vm.stopPrank();
// setting up the user
address someUser = makeAddr("someUser");
uint256 userStartingPoolTokenBalance = 100 ether;
poolToken.mint(someUser, userStartingPoolTokenBalance);
vm.prank(someUser);
poolToken.approve(address(pool), type(uint256).max);
// user intends to sell 5 pool tokens
vm.prank(someUser);
uint256 poolTokensToSell = 5e18;
// @note that sellPoolTokens() uses swapExactOutput() to perform the swap,
// which in turn calls getInputAmountBasedOnOutput() to calculate the amount of input tokens to be
// deducted from the user, and this function miscalculates the fee, so to make things worse,
// the user becomes subject of overtaxing too
pool.sellPoolTokens(poolTokensToSell);
uint256 expectedEndingUserPoolTokenBalance = userStartingPoolTokenBalance - poolTokensToSell;
uint256 realEndingUserPoolTokenBalance = poolToken.balanceOf(someUser);
console.log("Expected pool token balance of the user: ", expectedEndingUserPoolTokenBalance);
console.log("Real pool token balance of the user: ", realEndingUserPoolTokenBalance);
assert(expectedEndingUserPoolTokenBalance > realEndingUserPoolTokenBalance);
}

Impact

Users will swap the incorrects amount of tokens, which severely discrupts the functionality of the protocol.

Tools Used

Manual review, Foundry.

Recommendations

Change the implementation to use swapExactInput instead of the swapExactOutput function. Note that this would require the sellPoolTokens function to accept an additional parameter (i.e. minOutputAmount to be passed to swapExactInput).

- function sellPoolTokens(uint256 poolTokenAmount) external returns (uint256 wethAmount) {
+ function sellPoolTokens(uint256 poolTokenAmount, uint256 minWethToReceive) external returns (uint256 wethAmount) {
- return swapExactOutput(i_poolToken, i_wethToken, poolTokenAmount, uint64(block.timestamp));
+ return swapExactInput(i_poolToken, i_wethToken, poolTokenAmount, minWethToReceive, uint64(block.timestamp));
}

Additionally, it might be wise to add a deadline to the function, as currently there is no deadline.

Updates

Appeal created

inallhonesty Lead Judge about 1 year ago
Submission Judgement Published
Validated
Assigned finding tags:

`sellPoolTokens` mismatches input and output tokens causing users to receive the incorrect amount of tokens

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.