First Flight #18: T-Swap

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

Incorrect fee calculation in `TSwapPool::getInputAmountBasedOnOutput` causes protocll to take too many tokens from users, resulting in lost fees

Description: The getInputAmountBasedOnOutput function is intended to calculate the amount of tokens a user should deposit given an amount of tokens of output tokens. However, the function currently miscalculates the resulting amount. When calculating the fee, it scales the amount by 10_000 instead of 1_000.

Impact: Protocol takes more fees than expected from users.

Proof of Concept: To test this, include the following code in the TSwapPool.t.sol file:

POC
function testFlawedSwapExactOutput() 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);
uint256 expected = 9e18;
poolToken.approve(address(pool), 10e18);
pool.swapExactInput(poolToken, 10e18, weth, expected, uint64(block.timestamp));
vm.stopPrank();
vm.startPrank(liquidityProvider);
pool.approve(address(pool), 100e18);
pool.withdraw(100e18, 90e18, 100e18, uint64(block.timestamp));
assertEq(pool.totalSupply(), 0);
assert(weth.balanceOf(liquidityProvider) + poolToken.balanceOf(liquidityProvider) > 400e18);
}
function testFlawedSwapExactOutput() public {
uint256 initialLiquidity = 100e18;
vm.startPrank(liquidityProvider);
console.log("liquidityProvider weth balance before deposit:", weth.balanceOf(address(liquidityProvider)));
console.log(
"liquidityProvider poolToken balance after deposit:", poolToken.balanceOf(address(liquidityProvider))
);
weth.approve(address(pool), initialLiquidity);
poolToken.approve(address(pool), initialLiquidity);
pool.deposit({
wethToDeposit: initialLiquidity,
minimumLiquidityTokensToMint: 0,
maximumPoolTokensToDeposit: initialLiquidity,
deadline: uint64(block.timestamp)
});
console.log("liquidityProvider weth balance after deposit:", weth.balanceOf(address(liquidityProvider)));
console.log("liquidityProvider poolToken balance after deposit:", weth.balanceOf(address(liquidityProvider)));
vm.stopPrank();
// User has 11 pool tokens
address someUser = makeAddr("someUser");
uint256 userInitialPoolTokenBalance = 11e18;
poolToken.mint(someUser, userInitialPoolTokenBalance);
console.log("someUser poolToken balance before withdrew:", poolToken.balanceOf(address(someUser)));
vm.startPrank(someUser);
// Users buys 1 WETH from the pool, paying with pool tokens
poolToken.approve(address(pool), type(uint256).max);
pool.swapExactOutput(poolToken, weth, 1 ether, uint64(block.timestamp));
assertLt(poolToken.balanceOf(someUser), 1 ether);
vm.stopPrank();
vm.startPrank(liquidityProvider);
pool.withdraw(
pool.balanceOf(liquidityProvider),
1, // minWethToWithdraw
1, // minPoolTokensToWithdraw
uint64(block.timestamp)
);
console.log("liquidityProvider weth balance after withdrew:", weth.balanceOf(address(liquidityProvider)));
console.log(
"liquidityProvider poolToken balance after withdrew:", poolToken.balanceOf(address(liquidityProvider))
);
assertEq(weth.balanceOf(address(pool)), 0);
assertEq(poolToken.balanceOf(address(pool)), 0);
console.log("someUser poolToken balance after withdrew:", poolToken.balanceOf(address(someUser)));
}

Recommended Mitigation:

function getInputAmountBasedOnOutput(
uint256 outputAmount,
uint256 inputReserves,
uint256 outputReserves
)
public
pure
revertIfZero(outputAmount)
revertIfZero(outputReserves)
returns (uint256 inputAmount)
{
- return ((inputReserves * outputAmount) * 10_000) / ((outputReserves - outputAmount) * 997);
+ return ((inputReserves * outputAmount) * 1_000) / ((outputReserves - outputAmount) * 997);
}
Updates

Appeal created

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

Incorrect fee calculation in TSwapPool::getInputAmountBasedOnOutput causes protocol to take too many tokens from users, resulting in lost fees

Support

FAQs

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

Give us feedback!