Skip to content

Latest commit

 

History

History
64 lines (49 loc) · 4.4 KB

062.md

File metadata and controls

64 lines (49 loc) · 4.4 KB

Clean Indigo Stallion

High

ETH Refund Mechanism in streamEscrow::cancelStream Allows Noun Owners to Reclaim Funds Based on Market conditions, Exposing DAO to Potential Losses

Summary

Currently, Noun owners can call the streamEscrow::cancelStream function to withdraw any remaining ETH before the stream reaches maturity. Given that each stream duration is up to 4 years, this provides Noun owners with a prolonged window to give back their NFT if the market value of the NFT depreciates below the amount of ETH locked, thereby exposing NounsDAO to significant financial risk over the long term.

Root Cause

The streamEscrow::cancelStream function allows a Noun owner to reclaim any remaining ETH from the stream and return the Noun to the nounsRecipient. https://github.com/sherlock-audit/2024-11-nounsdao/blob/main/nouns-monorepo/packages/nouns-contracts/contracts/StreamEscrow.sol#L167-L186

        //@audit give back the nounId to nounsRecipient
        nounsToken.transferFrom(msg.sender, nounsRecipient, nounId);

        //@audit cancel the stream here
        streams[nounId].canceled = true;
        Stream memory stream = streams[nounId];
        ethStreamedPerTick -= stream.ethPerTick;
        ethStreamEndingAtTick[stream.lastTick] -= stream.ethPerTick;

        // calculate how much needs to be refunded
        uint256 ticksLeft = stream.lastTick - currentTick;
        uint256 amountToRefund = stream.ethPerTick * ticksLeft;
        //@audit refund ETH to noun owner
        (bool sent, ) = msg.sender.call{ value: amountToRefund }('');
        require(sent, 'failed to send eth');

As shown in the code above, the stream is first canceled, and then the remaining ETH is calculated by subtracting stream.lastTick from currentTick, and multiplying the result by stream.ethPerTick. This means that if 2 years remain in the stream, and the Noun owner initially locked 80 ETH, they would receive 40 ETH back after those 2 years.

Internal pre-conditions

External pre-conditions

Attack Path

In this scenario, the ETH price increases while the NFT price crashes. This creates an opportunity for the user to reclaim more ETH than the NFT is worth: 1- The user places the highest bid in the current auction and wins Noun #88 for 100 ETH. 2- 80 ETH is locked in streamEscrow and is released gradually over 4 years. 3- After 1 year, the user's Noun price drops significantly, but ETH price has increased 5X. 4- The user sees that 3/4 of their ETH is still locked in the stream, now worth much more than the Noun. 5- The user cancels the stream, withdraws the ETH, and returns the now underpriced Noun.

Impact

NounsDAO faces significant losses by giving Noun owners a 4 years window to cancel the stream and reclaim their remaining ETH whenever the value of the NFT drops well below the locked ETH. This design allows Noun owners to withdraw remaining ETH whenever the market value of their NFT drops below the amount they’ve locked in. For example, if a user wins an auction with a 100 ETH bid and locks 80 ETH (after the 20% treasury fee), they are committing the ETH to the stream for 4 years. In this volatile crypto market, ETH can fluctuate significantly (e.g. ETH increased from 2200 to 3500 in just one month). After 2 years, if the NFT price drops below 10 ETH but the user still has 40 ETH remaining in the stream, they can easily withdraw that ETH and return the underpriced NFT.

PoC


Mitigation

There are several ways to mitigate this issue, i will list some of them here: 1- Limit the amount of locked ETH that can be withdrawn. 2- Allow ETH to be withdrawn only within a specific window after the auction. 3- Implement a cooldown period between withdrawals, and allow only partial withdrawals at a time. 4- Prevent the return of ETH if the NFT’s market value is significantly below its locked ETH.