Platform: Code4rena
Start Date: 24/03/2023
Pot Size: $49,200 USDC
Total HM: 20
Participants: 246
Period: 6 days
Judge: Picodes
Total Solo HM: 1
Id: 226
League: ETH
Rank: 193/246
Findings: 1
Award: $13.13
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: brgltd
Also found by: 0x3b, 0xAgro, 0xGusMcCrae, 0xNorman, 0xRajkumar, 0xSmartContract, 0xTraub, 0xWagmi, 0xWaitress, 0xffchain, 0xhacksmithh, 0xkazim, 0xnev, 3dgeville, ArbitraryExecution, Aymen0909, BRONZEDISC, Bason, Bloqarl, BlueAlder, Brenzee, CodeFoxInc, CodingNameKiki, Cryptor, DadeKuma, DevABDee, Diana, Dug, Englave, Gde, Haipls, HollaDieWaldfee, Ignite, Infect3d, Jerry0x, Josiah, Kaysoft, Koko1912, KrisApostolov, Lavishq, LeoGold, Madalad, PNS, Rappie, RaymondFam, RedTiger, Rickard, Rolezn, Sathish9098, SunSec, T1MOH, UdarTeam, Udsen, Viktor_Cortess, Wander, adriro, ak1, alejandrocovrr, alexzoid, arialblack14, ayden, bin2chen, brevis, btk, c3phas, carlitox477, catellatech, ch0bu, chaduke, ck, climber2002, codeslide, descharre, dingo2077, ernestognw, fatherOfBlocks, favelanky, georgits, helios, hl_, inmarelibero, juancito, ks__xxxxx, lopotras, lukris02, m_Rassska, mahdirostami, maxper, nadin, navinavu, nemveer, p_crypt0, peanuts, pipoca, pixpi, qpzm, rbserver, reassor, roelio, rotcivegaf, scokaf, siddhpurakaran, slvDev, smaul, tnevler, tsvetanovv, turvy_fuzz, vagrant, wen, yac, zzzitron
13.1298 USDC - $13.13
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L170-L173 https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L190-L193 The mentioned code fragments are repeated, instead put it in a method. (Don't repeat the code, Write Code Once)
Recommended Solution
1 . Create a new function:
function recalculateTotalWeight() private { uint256 localTotalWeight = 0; for (uint256 i; i < derivativeCount; ++i) localTotalWeight += weights[i]; totalWeight = localTotalWeight; }
2 . Call the function wherever you need, for example:
function addDerivative( address _contractAddress, uint256 _weight ) external onlyOwner { derivatives[derivativeCount] = IDerivative(_contractAddress); weights[derivativeCount] = _weight; derivativeCount++; recalculateTotalWeight(); // <<--------- Call the function --------------- emit DerivativeAdded(_contractAddress, _weight, derivativeCount); }
function adjustWeight( uint256 _derivativeIndex, uint256 _weight ) external onlyOwner { weights[_derivativeIndex] = _weight; recalculateTotalWeight(); // <<--------- Call the function ------------- emit WeightChange(_derivativeIndex, _weight); }
totalWeight
in addDerivative
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L190-L193
The mentioned code lines can be replaced with a single line of code.
addDerivative
function can be changed as follows:
function addDerivative( address _contractAddress, uint256 _weight ) external onlyOwner { derivatives[derivativeCount] = IDerivative(_contractAddress); weights[derivativeCount] = _weight; derivativeCount++; totalWeight += _weight; // <<----------- This line should be enough ---------- emit DerivativeAdded(_contractAddress, _weight, derivativeCount); }
ethPerDerivative
I think the argument passed to the function ethPerDerivative
is inappropriate, i mean derivative[i].balance()
.
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/SafEth.sol#L73
function stake() external payable { // ... uint256 underlyingValue = 0; // Getting underlying value in terms of ETH for each derivative for (uint i = 0; i < derivativeCount; i++) underlyingValue += (derivatives[i].ethPerDerivative(derivatives[i].balance()) * // <<-------- derivative[i].balance() is passed to ethPerDerivative derivatives[i].balance()) / 10 ** 18; // ... }
Let's look at ethPerDerivative
function in Reth contract (the argument is unused in other derivative contracts)
The parameter is called _amount
, so the Reth contract is checking the _amount
can be deposited to the RocketPool or not ? if yes it will fetch the RETH token price from the RocketTokenRETH and otherwise it fetches the price from the Uniswap pool:
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L211-L216
function ethPerDerivative(uint256 _amount) public view returns (uint256) { if (poolCanDeposit(_amount)) // <<------ Reth is checking all of the contract balance can be deposited or not, because the _amount is set to derivative[i].balance() return RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18); else return (poolPrice() * 10 ** 18) / (10 ** 18); }
As we saw previously, the derivative[i].balance()
is passed to this function so _amount
is equal to Reth contract balance, and logically the Reth contract doesn't want to deposit as much as its balance every time stake
function is called, so why it is checking that all of the balance is depositable ?
In other word, when a user calls the stake
function, Reth contract is not checking the user's ethers can be deposited or not ?! Instead, it every time checks all of the contract balance can be deposited or not.
derivative[i].balance()
should be replaced with msg.value
like this one:
https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L170
#0 - c4-sponsor
2023-04-10T19:26:46Z
elmutt marked the issue as sponsor confirmed
#1 - c4-judge
2023-04-24T18:52:48Z
Picodes marked the issue as grade-b