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: 145/246
Findings: 3
Award: $21.16
π Selected for report: 0
π Solo Findings: 0
π Selected for report: monrel
Also found by: 0xRajkumar, 0xfusion, AkshaySrivastav, Bahurum, Brenzee, Cryptor, Dug, Haipls, Koolex, Krace, MiloTruck, RaymondFam, RedTiger, ToonVH, Tricko, Vagner, aga7hokakological, anodaram, bart1e, bin2chen, bytes032, carrotsmuggler, ck, d3e4, giovannidisiena, igingu, juancito, mahdirostami, mert_eren, n33k, nemveer, parsely, pavankv, sashik_eth, shaka, sinarette, ulqiorra, yac
3.4908 USDC - $3.49
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L63-L101
In stake function we calculation mintAmount by "totalStakeValueEth * 10 ** 18) / preDepositPrice" where totalStakeValueEth is ether staked by user and preDepositPrice is calculated by "(10 ** 18 * underlyingValue) / totalSupply" where underlyingValue is previously staked ether. If simplify it we will have mintAmount equal to "totalSupply*totalStakeValueEth/underlyingValue".
Now first use can stake minimum eth which will be "totalWeight/weight+1" and weight will be higher from all. We have taken minimum eth equal to this because if first user provide less than this then he can have deposit ethAmount to zero and if he will have very high minimum eth then he may will not be able to perform attack.
1.First user will stake minimum eth. 2.When second user come then first user can front-run him by directly transfer more worth of staked in protocols to directly to Reth.sol, SfrxEth.sol and WstEth.sol. 3.First user will transfer more worth of staked eth to make underlyingValue greater than totalSupply*totalStakeValueEth(value staked by second user). 4.In this way upcoming users will have zero mintAmount because denominator will be greater than numerator.
Solidity visual developer
My recommendation step is that when mintAmount will be zero then stake function should revert.
#0 - c4-pre-sort
2023-03-31T18:00:08Z
0xSorryNotSorry marked the issue as low quality report
#1 - c4-pre-sort
2023-04-04T12:44:26Z
0xSorryNotSorry marked the issue as duplicate of #715
#2 - c4-judge
2023-04-21T14:56:23Z
Picodes marked the issue as satisfactory
π Selected for report: adriro
Also found by: 0xMirce, 0xRajkumar, 0xepley, BPZ, Bahurum, Bauer, Co0nan, Emmanuel, Franfran, HollaDieWaldfee, IgorZuk, MiloTruck, NoamYakov, RedTiger, Ruhum, T1MOH, Tricko, ad3sh_, auditor0517, bin2chen, carrotsmuggler, eyexploit, handsomegiraffe, igingu, jasonxiale, koxuan, lukris02, monrel, nadin, peanuts, rbserver, rvierdiiev, shaka, sinarette, tnevler, y1cunhui
4.5426 USDC - $4.54
The 'withdraw' function has a parameter called 'minOut', which is calculated by subtracting the maximum slippage percentage from the stEthBal. However, stEthBal represents the token amount of 'x' that we have, whereas we are actually exchanging it for token 'y'. This is a bug because we are passing the amount of token 'x', but we should be passing the amount of token 'y' instead. This mistake can cause a Denial of Service (DOS) if minOut is less than 'dy', and it can give an opportunity for an attacker to use front-running for instant profit if minOut is much higher than 'dy'.
We are calculating amount of minOut by using
uint256 minOut = (stEthBal * (10 ** 18 - maxSlippage)) / 10 ** 18;
Since stEthBal represents the amount of token 'x' (stEth) and we are exchanging it for token 'y' (ETH), we should pass the amount of 'y' instead of 'x'.
Solidity visual developer
My recommendation is use get_dy to get amount of y tokens and subtract maximum slippage percentage from it like
uint256 minOut = (get_dy(1, 0, stEthBal) * (10 ** 18 - maxSlippage)) / 10 ** 18;
#0 - c4-pre-sort
2023-04-02T13:29:40Z
0xSorryNotSorry marked the issue as low quality report
#1 - c4-pre-sort
2023-04-04T21:29:16Z
0xSorryNotSorry marked the issue as duplicate of #588
#2 - c4-judge
2023-04-21T17:07:11Z
Picodes marked the issue as satisfactory
#3 - c4-judge
2023-04-23T11:07:04Z
Picodes changed the severity to 3 (High Risk)
π 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/main/contracts/SafEth/SafEth.sol#L194
We should pass derivativeCount-- instead of derivativeCount
function addDerivative( address _contractAddress, uint256 _weight ) external onlyOwner { derivatives[derivativeCount] = IDerivative(_contractAddress); weights[derivativeCount] = _weight; derivativeCount++; uint256 localTotalWeight = 0; for (uint256 i = 0; i < derivativeCount; i++) localTotalWeight += weights[i]; totalWeight = localTotalWeight; emit DerivativeAdded(_contractAddress, _weight, derivativeCount--); }
splitting privileges (e.g. via the multisig option) to ensure that no one address has excessive ownership of the system, clearly documenting the functions and implementations the owner can change, documenting the risks associated with privileged users and single points of failure, and ensuring that users are aware of all the risks associated with the system.
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L84 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L113
Bounding the loop where possible to avoid unnecessary gas wastage and denial of service.
for (uint i = 0; i < derivativeCount; i++) underlyingValue += (derivatives[i].ethPerDerivative(derivatives[i].balance()) * derivatives[i].balance()) / 10 ** 18; uint256 totalSupply = totalSupply(); uint256 preDepositPrice; // rice of safETH in regards to ETH if (totalSupply == 0) preDepositPrice = 10 ** 18; // initializes with a price of 1 else preDepositPrice = (10 ** 18 * underlyingValue) / totalSupply;
ethPerDerivative = 1232427936278277338 balance = 1132454354667767865 totalsupply = 1029343465645635565 Without precision loss = 1355882103333836962 With precision loss = 1355882103333836961
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L2 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L2 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L2 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L2
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/WstEth.sol#L48 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L51 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/Reth.sol#L58
Note that external functions require parameters with a data location of calldata.
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L48
We can save some gas also by marking it as calldata
#0 - c4-sponsor
2023-04-10T19:04:49Z
toshiSat marked the issue as sponsor confirmed
#1 - c4-judge
2023-04-24T17:47:35Z
Picodes marked the issue as grade-b