Summary
When TSwapPool::sellPoolTokens
is called, depending on the balance of the user the swap fails.
Vulnerability Details
Inside the TSwapPool::sellPoolTokens
, the function TSwapPool::swapExactOutput
is called, as we can see below:
function sellPoolTokens(
uint256 poolTokenAmount
) external returns (uint256 wethAmount) {
return
swapExactOutput(
i_poolToken,
i_wethToken,
poolTokenAmount,
uint64(block.timestamp)
);
}
But when the TSwapPool::swapExactOutput
is called, is passed the third parameter poolTokenAmount
, it means that user want to receive this quantity in weth token, but user may not have this quantity in pool tokens to sell, getting an error of ERC20InsufficientBalance
.
We need to call TSwapPool::swapExactInput
instead of TSwapPool:swapExactOutput
because we only know the quantity that user want to sell in pool token.
Impact
The user may not sell your pool tokens getting and ERC20InsufficientBalance
error.
Tools Used
Solidity and Foundry
Proof of Concept
Add the folloing PoC to test/unit/TSwapPool.t.sol
:
function testSellPoolTokens() public {
vm.startPrank(liquidityProvider);
weth.approve(address(pool), 100e18);
poolToken.approve(address(pool), 100e18);
pool.deposit(100e18, 100e18, 100e18, uint64(block.timestamp));
vm.stopPrank();
vm.startPrank(user);
poolToken.approve(address(pool), 100_000e18);
uint256 userPoolTokenInitialBalance = poolToken.balanceOf(user);
uint256 userWethInitialBalance = weth.balanceOf(user);
uint256 expectedUserWethFinalBalance = userWethInitialBalance
+ pool.getOutputAmountBasedOnInput(
userPoolTokenInitialBalance, poolToken.balanceOf(address(pool)), weth.balanceOf(address(pool))
);
pool.sellPoolTokens(userPoolTokenInitialBalance);
assertEq(poolToken.balanceOf(user), 0, "The balance of pool tokens should be 0");
assertEq(
weth.balanceOf(user) == expectedUserWethFinalBalance,
true,
"The balance of WETH should be equal the expected balance"
);
}
Recommendations
You need to call the TSwapPool:swapExactInput
to make the swap correctly, for example:
function sellPoolTokens(
uint256 poolTokenAmount
) external returns (uint256 wethAmount) {
- return
- swapExactOutput(
- i_poolToken,
- i_wethToken,
- poolTokenAmount,
- uint64(block.timestamp)
- );
+ uint256 minOutputAmount = getOutputAmountBasedOnInput(
+ poolTokenAmount, i_poolToken.balanceOf(address(this)), i_wethToken.balanceOf(address(this))
+ );
+ return swapExactInput(
+ i_poolToken,
+ poolTokenAmount,
+ i_wethToken,
+ minOutputAmount,
+ uint64(block.timestamp)
+ );
}