Closed code423n4 closed 1 year ago
0xSorryNotSorry marked the issue as primary issue
toshiSat marked the issue as sponsor disputed
We are returning value in ETH due to price oracle
Picodes marked issue #1108 as primary and marked this issue as a duplicate of 1108
Picodes marked the issue as not a duplicate
Picodes marked the issue as duplicate of #641
Picodes marked the issue as satisfactory
@Picodes the warden incorrectly assumes we are returning in FRX domination, but we aren't, it's in ETH
Hi @toshiSat, sorry for any confusion. Perhaps, taking a look at the duplicates of this issue's primary issue like #141 can help. Basically, in the SfrxEth.ethPerDerivative
function, dividing (10 ** 18 * frxAmount)
by IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).price_oracle()
does not return a value that is denominated in ETH. Because (10 ** 18 * frxAmount) / IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).price_oracle()
would not be denominated in ETH, it can be seen as (scalar_1 * frxAmount) / scalar_2
so I mentioned that a value that is denominated in frxETH instead of ETH is used. Thanks!
Lines of code
https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L111-L117 https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L60-L88 https://etherscan.io/address/0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577#code#L516
Vulnerability details
Impact
The following
SfrxEth.ethPerDerivative
function returns how much frxETH that 10 ** 18 sfrxETH are worth.https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L111-L117
In the following
SfrxEth.withdraw
function, sinceminOut
is set to(((ethPerDerivative(_amount) * _amount) / 10 ** 18) * (10 ** 18 - maxSlippage)) / 10 ** 18
,minOut
is denominated in frxETH instead of ETH. SuchminOut
is used to callIFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(1, 0, frxEthBalance, minOut)
for exchanging frxETH for ETH.https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/derivatives/SfrxEth.sol#L60-L88
For the corresponding frxETH-ETH pool contract's
exchange
function below, the_min_dy
input is the minimum amount of thej
input's asset to receive. When callingIFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(1, 0, frxEthBalance, minOut)
, the asset corresponding to thej
input should be ETH and the_min_dy
input needs to be the minimum amount of ETH to receive. However,minOut
is denominated in frxETH instead of ETH. If 1 frxETH is worth more than 1 ETH at this moment, then_min_dy
based onminOut
denominated in frxETH is less than_min_dy
based onminOut
denominated in ETH. Yet, becauseminOut
, which is denominated in frxETH instead of ETH, is used, frxETH can be exchanged to ETH at a very suboptimal exchange rate whenIFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(1, 0, frxEthBalance, minOut)
is called. As a result, the slippage control becomes incorrect, and users lose ETH when they should not.https://etherscan.io/address/0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577#code#L516
Proof of Concept
The following steps can occur for the described scenario. 1.The
SfrxEth
contract is the only derivative so far.SafEth.unstake
function, which further calls theSfrxEth.withdraw
function.SfrxEth.withdraw
function,minOut
is calculated to be 10e18 frxETH. WhenminOut
is 10e18, callingIFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(1, 0, frxEthBalance, minOut)
allows 10e18 ETH to be exchanged and eventually sent to Alice.IFrxEthEthPool(FRX_ETH_CRV_POOL_ADDRESS).exchange(1, 0, frxEthBalance, minOut)
can be called withminOut
being 11e18, such function call should revert if only 10e18 ETH can be exchanged.Tools Used
VSCode
Recommended Mitigation Steps
The
SfrxEth.ethPerDerivative
function can be updated to return how much ETH instead of frxETH that 10 ** 18 sfrxETH are worth.