Platform: Code4rena
Start Date: 04/03/2024
Pot Size: $36,500 USDC
Total HM: 9
Participants: 80
Period: 7 days
Judge: hansfriese
Total Solo HM: 2
Id: 332
League: ETH
Rank: 71/80
Findings: 1
Award: $1.47
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: DarkTower
Also found by: 0xJaeger, 0xJoyBoy03, 0xRiO, 0xkeesmark, 0xlemon, 0xmystery, Abdessamed, AcT3R, Afriauditor, AgileJune, Al-Qa-qa, Aymen0909, Daniel526, DanielTan_MetaTrust, Dots, FastChecker, Fitro, GoSlang, Greed, Krace, McToady, SoosheeTheWise, Tripathi, asui, aua_oo7, btk, crypticdefense, d3e4, dd0x7e8, dvrkzy, gesha17, iberry, kR1s, leegh, marqymarq10, n1punp, pa6kuda, radin100, sammy, smbv-1923, trachev, turvy_fuzz, valentin_s2304, wangxx2026, y4y, yotov721, yvuchev, zhaojie
1.4652 USDC - $1.47
Yield that was supposed to be granted to users (or could have been granted to the feeRecipient
) can't be claimed anymore.
When the yieldVault
earns yield, the feeRecipient
can call claimYieldFeeShares()
to earn shares of the PrizeVault
.
function claimYieldFeeShares(uint256 _shares) external onlyYieldFeeRecipient { if (_shares == 0) revert MintZeroShares(); uint256 _yieldFeeBalance = yieldFeeBalance; if (_shares > _yieldFeeBalance) revert SharesExceedsYieldFeeBalance(_shares, _yieldFeeBalance); yieldFeeBalance -= _yieldFeeBalance; _mint(msg.sender, _shares); emit ClaimYieldFeeShares(msg.sender, _shares); }
The claimable shares can't exceed the yieldFeeBalance
, which is increased in the transferTokensOut()
function.
function transferTokensOut( address, address _receiver, address _tokenOut, uint256 _amountOut ) public virtual onlyLiquidationPair returns (bytes memory) { // ... // Increase yield fee balance: if (_yieldFee > 0) { yieldFeeBalance += _yieldFee; } // ... }
The issue occurs when claimYieldFeeShares()
is called with a parameter that is less than the yieldFeeBalance
.
In that case, the shares of all the users won't match the amount of assets stored in the yieldVault
which will result in some assets to be stuck in, with no way to claim them.
The following test can be added to Liquidate.t.sol
and is based upon testClaimYieldFeeShares_withdrawFullBalance
.
function testClaimYieldFeeShares_breakRatio() public { vault.setYieldFeePercentage(1e8); // 10% fee vault.setYieldFeeRecipient(bob); vault.setLiquidationPair(address(this)); // Alice deposits assets deal(address(underlyingAsset), alice, 1e18); console.log("Alice assets before : ", underlyingAsset.balanceOf(alice)); vm.startPrank(alice); console.log("[*] Alice deposits..."); underlyingAsset.approve(address(vault), 1e18); vault.deposit(underlyingAsset.balanceOf(alice), alice); console.log("Alice shares obtained : ", vault.balanceOf(alice)); console.log("Alice assets after deposit : ", underlyingAsset.balanceOf(alice)); vm.stopPrank(); // liquidate some yield underlyingAsset.mint(address(vault), 1e18); uint256 amountOut = vault.liquidatableBalanceOf(address(underlyingAsset)); assertGt(amountOut, 0); vault.transferTokensOut(address(0), alice, address(underlyingAsset), amountOut); uint256 yieldFeeBalance = vault.yieldFeeBalance(); assertGt(yieldFeeBalance, 0); vm.startPrank(bob); console.log("[*] Bob claims only 1 yield fee shares"); vault.claimYieldFeeShares(1); vm.stopPrank(); // Since bob has some shares, he can withdraw some assets vm.startPrank(bob); console.log("[*] Bob obtained shares and now withdraws some assets"); vault.withdraw(1, bob, bob); console.log("Bob assets obtained : ", underlyingAsset.balanceOf(bob)); vm.stopPrank(); // Alice withdraws her assets, making a profit but there is yield stuck in yield vault vm.startPrank(alice); console.log("[*] Alice withdraws her initial deposit"); vault.withdraw(1e18, alice, alice); console.log("Alice assets after : ", underlyingAsset.balanceOf(alice)); vm.stopPrank(); console.log("Assets stuck in yield vault :", underlyingAsset.balanceOf(address(vault.yieldVault()))); console.log("Shares of Bob :", vault.balanceOf(bob)); console.log("Shares of Alice :", vault.balanceOf(alice)); }
Manual analysis
Foundry tests
Automatically mint the corresponding shares to the fee recipient in the claimYieldFeeShares()
Token-Transfer
#0 - c4-pre-sort
2024-03-11T21:42:52Z
raymondfam marked the issue as sufficient quality report
#1 - c4-pre-sort
2024-03-11T21:42:57Z
raymondfam marked the issue as duplicate of #10
#2 - c4-pre-sort
2024-03-13T04:38:16Z
raymondfam marked the issue as duplicate of #59
#3 - c4-judge
2024-03-15T07:40:25Z
hansfriese marked the issue as satisfactory