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: 67/246
Findings: 1
Award: $81.32
π Selected for report: 0
π Solo Findings: 0
π Selected for report: CodingNameKiki
Also found by: 0xd1r4cde17a, Franfran, MadWookie, MiloTruck, Moliholy, adriro, ast3ros, bin2chen, giovannidisiena, gjaldon, igingu, koxuan, rbserver, rvierdiiev, shaka, slippopz
81.3214 USDC - $81.32
https://github.com/code-423n4/2023-03-asymmetry/blob/0e826f307866f3b9e737a3fd7b860e1df2dd7e71/contracts/SafEth/SafEth.sol#L73 https://github.com/code-423n4/2023-03-asymmetry/blob/0e826f307866f3b9e737a3fd7b860e1df2dd7e71/contracts/SafEth/SafEth.sol#L92-L94 https://github.com/code-423n4/2023-03-asymmetry/blob/0e826f307866f3b9e737a3fd7b860e1df2dd7e71/contracts/SafEth/derivatives/Reth.sol#L212-L215
Late stakers may gain an unfair advantage compared to early stakers when staking to the SafETH contract. This can happen because ethPerDerivative
may be calculated differently in underlyingValue
and derivativeReceivedEthValue
when depositing to Reth derivative.
When calculating the amount of SafETH to mint:
underlyingValue
is calculated using the whole balance in the derivative contract as input for the ethPerDerivative function: derivatives[i].ethPerDerivative(derivatives[i].balance())
.underlyingValue += (derivatives[i].ethPerDerivative(derivatives[i].balance()) * derivatives[i].balance()) / 10 ** 18;
The input for ethPerDerivative function is whole balance in derivative contract: derivatives[i].ethPerDerivative(derivatives[i].balance())
derivativeReceivedEthValue
is calculated using only the depositAmount as input for the ethPerDerivative function: derivative.ethPerDerivative(depositAmount)
.uint derivativeReceivedEthValue = (derivative.ethPerDerivative( depositAmount ) * depositAmount) / 10 ** 18;
ethPerDerivative
will be taken from either UniswapV3 or RocketTokenPool.if (poolCanDeposit(_amount)) return RocketTokenRETHInterface(rethAddress()).getEthValue(10 ** 18); else return (poolPrice() * 10 ** 18) / (10 ** 18);
Therefore, the value of ethPerDerivative
in (1) can be different from (2) because derivatives[i].balance()
can be significantly different than depositAmount
.
The following scenario is likely to happen:
derivatives[i].balance()
is large, therefore ethPerDerivative is derived from UniswapV3. 1 RETH = 1.0661 WETH (price at time T 28/03).depositAmount
is small, therefore ethPerDerivative is derived from RocketTokenPool. 1 RETH = 1.06911 WETH (price at time T 28/03).This leads to underlyingValue
being understated or derivativeReceivedEthValue
being overstated, which in turn leads to late stakers receiving more SafETH.
Manual
Using the same price when calculating the underlyingValue
and the derivativeReceivedEthValue
for RETH derivative when calculating the mintAmount
to remain ratio valueETH/totalSupply
.
When calculating the mintAmount
, itβs important to use the same price when calculating both the underlyingValue
and the derivativeReceivedEthValue
for RETH derivative. It helps remain underlyingValue/totalSupply
.
#0 - c4-pre-sort
2023-04-04T17:46:05Z
0xSorryNotSorry marked the issue as duplicate of #1004
#1 - c4-judge
2023-04-21T14:03:53Z
Picodes marked the issue as duplicate of #1125
#2 - c4-judge
2023-04-21T14:19:09Z
Picodes marked the issue as satisfactory
#3 - c4-judge
2023-04-21T14:19:22Z
Picodes marked the issue as not a duplicate
#4 - c4-judge
2023-04-21T14:19:34Z
Picodes marked the issue as duplicate of #1004
#5 - c4-judge
2023-04-24T21:40:08Z
Picodes changed the severity to 3 (High Risk)