Escher contest - gz627's results

A decentralized curated marketplace for editioned artwork.

General Information

Platform: Code4rena

Start Date: 06/12/2022

Pot Size: $36,500 USDC

Total HM: 16

Participants: 119

Period: 3 days

Judge: berndartmueller

Total Solo HM: 2

Id: 189

League: ETH

Escher

Findings Distribution

Researcher Performance

Rank: 86/119

Findings: 2

Award: $2.18

🌟 Selected for report: 0

🚀 Solo Findings: 0

Lines of code

https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L124

Vulnerability details

Impact

In LPDA, price is calculated as: price = sale.startPrice - (sale.dropPerSecond * timeElapsed). if sale.dropPerSecond > sale.startPrice/(sale.endTime - sale.startTime), the price will drop to negative before sale.endTime arrives. If the amount of sold items has not reached the amount of auction items before sale.price drops to negative, the auction will fail and will never end. This is because when calculating the sale.price, it will underflow, leading to transaction revert.

Proof of Concept

In the below test case, the 2nd purchase will revert since the sale.price has dropped to negative value at this time.

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "forge-std/Test.sol";
import {EscherTest} from "./utils/EscherTest.sol";
import {LPDAFactory, LPDA} from "src/minters/LPDAFactory.sol";

contract LPDAPriceDropTooFastBase is EscherTest {
    LPDAFactory public lpdaSales;
    LPDA.Sale public lpdaSale;

    function setUp() public virtual override {
        super.setUp();
        lpdaSales = new LPDAFactory();
        // set up a LPDA Sale
        lpdaSale = LPDA.Sale({
            currentId: uint48(0),
            finalId: uint48(10),
            edition: address(edition),
            startPrice: uint80(uint256(1 ether)),
            finalPrice: uint80(uint256(0.1 ether)),
            dropPerSecond: uint80(uint256(1 ether) / 10 hours),
            startTime: uint96(block.timestamp),
            saleReceiver: payable(address(69)),
            endTime: uint96(block.timestamp + 1 days)
        });
    }
}

contract LPDAPriceDropTooFastTest is LPDAPriceDropTooFastBase {
    LPDA public sale;

    function test_PriceDropTooFast_LPDA() public {
        // make the lpda sales contract
        sale = LPDA(lpdaSales.createLPDASale(lpdaSale));
        // authorize the lpda sale to mint tokens
        edition.grantRole(edition.MINTER_ROLE(), address(sale));
        // 1st buy success
        sale.buy{value: 1 ether}(1);
        assertEq(address(sale).balance, 1 ether);

        vm.warp(block.timestamp + 12 hours);

        vm.expectRevert();

        // 2nd purchase revert due to sale.price underflow
        sale.buy{value: uint256((1 ether) * 8)}(8);
    }
}

Tools Used

Manual audit.

Add prameter verification to make sure sale.price > 0 at sale.endTime, i.e. to make sure sale.dropPerSecond < sale.startPrice/(sale.endTime - sale.startTime).

#0 - c4-judge

2022-12-13T11:53:20Z

berndartmueller marked the issue as duplicate of #392

#1 - c4-judge

2023-01-02T19:57:12Z

berndartmueller marked the issue as satisfactory

Awards

1.3417 USDC - $1.34

Labels

bug
2 (Med Risk)
satisfactory
duplicate-328

External Links

Lines of code

https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L81

Vulnerability details

Impact

In some situations (e.g. the sales amount never reaches the amount for auction), LPDA auction sale may never end. When this happens, the seller/artist cannot get the sales imcome and the feeReceiver cannot receive the fee.

Proof of Concept

  1. There are only TWO reasons that can lead to auction completion: either the auction is successfully cancelled before it starts or the sales amount exactly reaches the auction amount.
  2. When an auction has started, most items have been selled. However, due to some reason (e.g. over supplied, or economic downturn, or digital market downtrun, or market divert... ), there are no people buying the auction items. This leads to everlasting waitng. The seller/artist cannot get the sales imcome and the feeReceiver cannot receive the fee.

Tools Used

Manual audit.

Some measures can be implemented to solve the problem. For example: The sale.saleReceiver can withdraw the current balance (minus fees), and at the same time transer the fees to feeReceiver; or alternatively, the feeReceiver can withdraw its current fees and at the same time transfer the remaining balance to sale.saleReceiver. or: The seller/artist have the ability to end the auction after some pre-specified time.

#0 - c4-judge

2022-12-12T09:02:25Z

berndartmueller marked the issue as duplicate of #328

#1 - c4-judge

2023-01-02T20:22:50Z

berndartmueller 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