Lucene search

K
code423n4Code4renaCODE423N4:2022-12-ESCHER-FINDINGS-ISSUES-471
HistoryDec 09, 2022 - 12:00 a.m.

There isn't an end time on FixedPrice and LPDA sales, and the sale can't be canceled after the sale start.

2022-12-0900:00:00
Code4rena
github.com
4
sale end time
nft sales
vulnerability fix

Lines of code
<https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/FixedPrice.sol#L14-L24&gt;
<https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L92-L96&gt;
<https://github.com/code-423n4/2022-12-escher/blob/5d8be6aa0e8634fdb2f328b99076b0d05fefab73/src/minters/LPDA.sol#L58-L89&gt;

Vulnerability details

Impact

The sale can’t be canceled after the sale start. Moreover, the sale won’t end, which contradict the fact that typical NFT sales nowadays have an end time. Even your OpenEdition have an end time.

Proof of Concept

    struct Sale {
        // slot 1
        uint48 currentId;
        uint48 finalId;
        address edition;
        // slot 2
        uint96 price;
        address payable saleReceiver;
        // slot 3
        uint96 startTime;
    }

FixedPrice sale doesn’t have an end time.

LPDA has an end time but it just trigger the end of LPDA price slope but not actual sale end time

    struct Sale {
        // slot 1
        uint72 price;
        uint24 currentId;
        address edition;
        // slot 2
        uint96 startTime;
        address payable saleReceiver;
        // slot 3
        uint96 endTime;
    }

In contrast, OpenEdition have an end time.

    /// @notice Owner can cancel current sale
    function cancel() external onlyOwner {
        require(block.timestamp &lt; sale.startTime, "TOO LATE");
        _end(sale);
    }

Moreover, cancel can’t be used after the sale has started.

As a result, once the sale has started, it never ends, which is not a best practice as typical NFT sales nowadays have an end time.

Recommended Mitigation Steps

Implement an end time logic to the FixedPrice sale

    struct Sale {
        // slot 1
        uint48 currentId;
        uint48 finalId;
        address edition;
        // slot 2
        uint96 price;
        address payable saleReceiver;
        // slot 3
        uint96 startTime;
        uint96 endTime;
    }

    ...

    /// @notice buy from a fixed price sale after the sale starts
    /// @param _amount the amount of editions to buy
    function buy(uint256 _amount) external payable {
        Sale memory sale_ = sale;
        IEscher721 nft = IEscher721(sale_.edition);
        require(block.timestamp &gt;= sale_.startTime, "TOO SOON");
        require(block.timestamp &lt; sale_.endTime, "TOO LATE");
        require(_amount * sale_.price == msg.value, "WRONG PRICE");
        uint48 newId = uint48(_amount) + sale_.currentId;
        require(newId &lt;= sale_.finalId, "TOO MANY");

        for (uint48 x = sale_.currentId + 1; x &lt;= newId; x++) {
            nft.mint(msg.sender, x);
        }

        sale.currentId = newId;

        emit Buy(msg.sender, _amount, msg.value, sale);

        if (newId == sale_.finalId) _end(sale);
    }

Similar pattern apply to LPDA sale too.


The text was updated successfully, but these errors were encountered:

All reactions