Asymmetry contest - 0xMirce's results

A protocol to help diversify and decentralize liquid staking derivatives.

General Information

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

Asymmetry Finance

Findings Distribution

Researcher Performance

Rank: 111/246

Findings: 2

Award: $33.34

🌟 Selected for report: 0

🚀 Solo Findings: 0

Awards

4.5426 USDC - $4.54

Labels

bug
3 (High Risk)
satisfactory
upgraded by judge
duplicate-588

External Links

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/WstEth.sol#L87

Vulnerability details

Impact

The WstEth contract incorrectly calculates the price of ETH in terms of WstETH.

The function returns the price of WstETH in terms of stETH. The underlying asset which we desire is ETH.

Since stETH does not have the same value as ETH the output price is incorrect.

The impact is severe as underlyingValue and totalStakeValueEth through the stake() function in the SafEth.sol contract SafEth.sol#L63 are wrong calculated.

Proof of Concept

The function IWstETH(token).getStETHByWstETH() only converts WstETH to stETH. Thus, ethPerDerivative() returns the value of WstETH in terms of stETH. Also, the @notice comment in the code says that the derivate's price should be in terms of ETH.

    /**
        @notice - Get price of derivative in terms of ETH
     */
    function ethPerDerivative(uint256 _amount) public view returns (uint256) {
        return IWStETH(WST_ETH).getStETHByWstETH(10 ** 18);
    }

Tools Used

Manuel review https://code4rena.com/reports/2022-05-alchemix/#m-15-lido-adapter-incorrectly-calculates-the-price-of-the-underlying-token

Add extra steps to ethPerDerivative() to approximate the rate for converting stETH to ETH. This can be done using the same curve pool that is used to convert stETH to ETH in withdraw().

#0 - c4-pre-sort

2023-04-04T17:14:23Z

0xSorryNotSorry marked the issue as duplicate of #588

#1 - c4-judge

2023-04-23T11:07:05Z

Picodes changed the severity to 3 (High Risk)

#2 - c4-judge

2023-04-24T20:45:28Z

Picodes marked the issue as satisfactory

Findings Information

Awards

28.8013 USDC - $28.80

Labels

bug
2 (Med Risk)
satisfactory
duplicate-812

External Links

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/44b5cd94ebedc187a08884a7f685e950e987261c/contracts/SafEth/derivatives/Reth.sol#L120

Vulnerability details

Impact

The main impact is that if a deposit is disabled in the rocketDepositPool contract, the deposit() function Reth.sol#L156 will revert, and not try to deposit through swapExactInputSingleHop and UniswapV3 contract Reth.sol#L170.

Function deposit() is called from stake() function SafEth.sol#L91, so, complete stake process will be reverted.

Proof of Concept

Function deposit() in RocketDepositPool.sol looks like

    function deposit() override external payable onlyThisLatestContract {
        // Check deposit settings
        RocketDAOProtocolSettingsDepositInterface rocketDAOProtocolSettingsDeposit = RocketDAOProtocolSettingsDepositInterface(getContractAddress("rocketDAOProtocolSettingsDeposit"));
        require(rocketDAOProtocolSettingsDeposit.getDepositEnabled(), "Deposits into Rocket Pool are currently disabled");
        require(msg.value >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit(), "The deposited amount is less than the minimum deposit size");
        RocketVaultInterface rocketVault = RocketVaultInterface(getContractAddress("rocketVault"));
        require(rocketVault.balanceOf("rocketDepositPool").add(msg.value) <= rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize(), "The deposit pool size after depositing exceeds the maximum size");
        // Calculate deposit fee
        uint256 depositFee = msg.value.mul(rocketDAOProtocolSettingsDeposit.getDepositFee()).div(calcBase);
        uint256 depositNet = msg.value.sub(depositFee);
        // Mint rETH to user account
        RocketTokenRETHInterface rocketTokenRETH = RocketTokenRETHInterface(getContractAddress("rocketTokenRETH"));
        rocketTokenRETH.mint(depositNet, msg.sender);
        // Emit deposit received event
        emit DepositReceived(msg.sender, msg.value, block.timestamp);
        // Process deposit
        processDeposit(rocketVault, rocketDAOProtocolSettingsDeposit);
    }

As it is seen from the code, it exists check require(rocketDAOProtocolSettingsDeposit.getDepositEnabled(), "Deposits into Rocket Pool are currently disabled");, so without this check, deposit() function in Reth.sol#L156 will revert.

Tools Used

Manuel review

In Reth.sol#L120 include check rocketDAOProtocolSettingsDeposit.getDepositEnabled(). The best place for that is Reth.sol#L146

    return
        rocketDAOProtocolSettingsDeposit.getDepositEnabled() &&
        rocketDepositPool.getBalance() + _amount <=
        rocketDAOProtocolSettingsDeposit.getMaximumDepositPoolSize() &&
        _amount >= rocketDAOProtocolSettingsDeposit.getMinimumDeposit();

#0 - c4-pre-sort

2023-04-04T18:55:47Z

0xSorryNotSorry marked the issue as duplicate of #812

#1 - c4-judge

2023-04-24T19:42:38Z

Picodes marked the issue as satisfactory

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter