Beginner FriendlyFoundryDeFiOracle
100 EXP
View results
Submission Details
Severity: high
Valid

Depositors who withdraw first get a disproportionate amount of the underlying tokens, stealing from depositors who withdraw later

Summary

Depositors who withdraw early get a disproportionate amount of the tokens

Test

I wrote this test in which three people deposit and then withdraw without any flash loaning in the interim. They should each get back the amount they put in, but the first two depositors get back more than they should, leaving the third depositor with less than they should get. Redeem reverts for the third person trying to redeem because there are not enough underlying tokens left to give them their full redemption. You have to add two additional prank LPs.

function testMultipleDepositsMultipleRedeems() public setAllowedToken {
AssetToken asset = thunderLoan.getAssetFromToken(tokenA);
tokenA.mint(liquidityProvider, DEPOSIT_AMOUNT);
uint256 lpInitialBalance = tokenA.balanceOf(liquidityProvider);
console.log("liquidityprovider initial balance:", lpInitialBalance);
tokenA.mint(lp2, AMOUNT);
uint256 lp2InitialBalance = tokenA.balanceOf(lp2);
console.log("lp2 initial balance:", lp2InitialBalance);
tokenA.mint(lp3, AMOUNT);
uint256 lp3InitialBalance = tokenA.balanceOf(lp3);
console.log("lp3 initial balance:", lp3InitialBalance);
vm.startPrank(liquidityProvider);
tokenA.approve(address(thunderLoan), DEPOSIT_AMOUNT);
thunderLoan.deposit(tokenA, DEPOSIT_AMOUNT);
uint256 lpAssetTokens = asset.balanceOf(liquidityProvider);
vm.stopPrank();
vm.startPrank(lp2);
tokenA.approve(address(thunderLoan), AMOUNT);
thunderLoan.deposit(tokenA, AMOUNT);
uint256 lp2AssetTokens = asset.balanceOf(lp2);
vm.stopPrank();
vm.startPrank(lp3);
tokenA.approve(address(thunderLoan), AMOUNT);
thunderLoan.deposit(tokenA, AMOUNT);
uint256 lp3AssetTokens = asset.balanceOf(lp3);
vm.stopPrank();
vm.startPrank(lp2);
thunderLoan.redeem(tokenA, lp2AssetTokens);
uint256 lp2EndingBalance = tokenA.balanceOf(lp2);
console.log("lp2 ending balance:", lp2EndingBalance);
vm.stopPrank();
vm.startPrank(liquidityProvider);
thunderLoan.redeem(tokenA, lpAssetTokens);
uint256 lpEndingBalance = tokenA.balanceOf(liquidityProvider);
console.log("lp ending balance:", lpEndingBalance);
vm.stopPrank();
vm.startPrank(lp3);
thunderLoan.redeem(tokenA, lp3AssetTokens);
uint256 lp3EndingBalance = tokenA.balanceOf(lp3);
console.log("lp3 ending balance:", lp3EndingBalance);
vm.stopPrank();
assertEq(lpInitialBalance, lpEndingBalance);
assertEq(lp2InitialBalance, lp2EndingBalance);
assertEq(lp3InitialBalance, lp3EndingBalance);
}

Here are the console logs...there is no ending balance for lp3 because redeem reverted because there wasn't enough tokens to send to lp3. You can see that lp2 and lp got more tokens than they should have:

liquidityprovider initial balance: 100000000000000000000
lp2 initial balance: 10000000000000000000
lp3 initial balance: 10000000000000000000
lp2 ending balance: 10005230000453930877
lp ending balance: 100352456904552926700

Impact

Some depositors get more than their fair share of the underlying tokens and others can't withdraw because there aren't sufficient tokens in the asset token contract to fully redeem them, so redeem reverts.

Tools Used

Foundry
VS Code

Recommendations

To figure out the cause of this, I tried running the same test but commented out these two lines in the deposit function and this time around the test passed. I recommend removing these lines.

//uint256 calculatedFee = getCalculatedFee(token, amount);
//assetToken.updateExchangeRate(calculatedFee);
Updates

Lead Judging Commences

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

can't redeem because of the update exchange rate

Support

FAQs

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