If there is "ever" a second depositer he will loose funds
Proof of Concept
First depositer can misuse furnace.melt() to skew basketNeeded & totalSupply such that all subsequent depositers loose funds.
function melt() public {
....
lastPayoutBal = rToken.balanceOf(address(this)) - amount;
///@audit ^ sets balance dynamically
if (amount != 0) rToken.melt(amount);
}
Scenario-
First depositer mints some dust amount of rToken lets say 40 (not 40e18)
He then proceeds to transfer half of them to furnace
lets say the ratio of furnace is high and it melts half of these funds in ~2 hours
So now basketsNeeded=40 but totalSupply = 30, i.e baskets needed 33% > totalSupply
So now in issueTo() amtBaskets will be 33% greater than what it should be as supply !=0
and this inflated amtBaskets will be used for input inside quote, leading to asking 33% more collateral than what it originally should but when redeeming it will only give him collateral worth of original amt. i.e if 2nd depositer mints 1000 rToken it will ask him to pay 1333 usd worth collateral tokens & when he redeems it will only give him 1000 usd worth collateral.
The coded POC shows how functionalities of protocol like quote() , redeem() will be broken leading to reverts & how only customRedemptions will work alongside with loses.
POC-
pay attention to the .to.be.reverted() calls, also the first furnace.melt() call is there just to set lastPayoutBal to a non 0 number, in this case it'll be, the melt() call after 2 hours is where the burning happens.
Furnace always need not melt 33% supply, even if he is able to run 1-2%(decreasing wait time for 2nd depositer) losses will be suffered.
POC can be ran after adding it to line 266 of Revenue.test.ts with command
PROTO_IMPL=1 npx hardhat test test/Revenues.test.ts --grep "should show second depositer funds loss"
Tools Used
Manual Review
Recommended Mitigation Steps
Keep internal accounting of rToken balance in furnace.sol , which should only be increased when Distributor.sol transfers tokens to it & only by the amount that distributor transferred as distributor already has checks that only contracts within system can call distribute()
Lines of code
https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/Furnace.sol#L65-L79 https://github.com/code-423n4/2024-07-reserve/blob/3f133997e186465f4904553b0f8e86ecb7bbacbf/contracts/p1/RToken.sol#L133-L143
Vulnerability details
Impact
If there is "ever" a second depositer he will loose funds
Proof of Concept
First depositer can misuse furnace.melt() to skew basketNeeded & totalSupply such that all subsequent depositers loose funds.
Scenario-
So now basketsNeeded=40 but totalSupply = 30, i.e baskets needed 33% > totalSupply So now in issueTo() amtBaskets will be 33% greater than what it should be as supply !=0
and this inflated amtBaskets will be used for input inside quote, leading to asking 33% more collateral than what it originally should but when redeeming it will only give him collateral worth of original amt. i.e if 2nd depositer mints 1000 rToken it will ask him to pay 1333 usd worth collateral tokens & when he redeems it will only give him 1000 usd worth collateral. The coded POC shows how functionalities of protocol like quote() , redeem() will be broken leading to reverts & how only customRedemptions will work alongside with loses. POC-
pay attention to the .to.be.reverted() calls, also the first furnace.melt() call is there just to set lastPayoutBal to a non 0 number, in this case it'll be, the melt() call after 2 hours is where the burning happens.
Furnace always need not melt 33% supply, even if he is able to run 1-2%(decreasing wait time for 2nd depositer) losses will be suffered. POC can be ran after adding it to line 266 of Revenue.test.ts with command
Tools Used
Manual Review
Recommended Mitigation Steps
Keep internal accounting of rToken balance in furnace.sol , which should only be increased when Distributor.sol transfers tokens to it & only by the amount that distributor transferred as distributor already has checks that only contracts within system can call distribute()
Assessed type
Error