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
Rank: 87/119
Findings: 2
Award: $2.18
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: adriro
Also found by: 0x446576, 0xA5DF, 0xDave, 0xDecorativePineapple, 0xRobocop, 0xbepresent, 8olidity, Aymen0909, Ch_301, Chom, Franfran, HollaDieWaldfee, Madalad, Parth, Ruhum, Tricko, bin2chen, carrotsmuggler, chaduke, danyams, evan, gz627, hansfriese, hihen, imare, immeas, jadezti, jayphbee, jonatascm, kaliberpoziomka8552, kiki_dev, kree-dotcom, ladboy233, lukris02, lumoswiz, mahdikarimi, minhquanym, minhtrng, nameruse, neumo, obront, pauliax, poirots, reassor, rvierdiiev, slvDev, sorrynotsorry, yixxas, zapaz
0.8413 USDC - $0.84
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L117-L125
If sale.dropPerSecond
is too large, the sale.price
will drop to negative value before the sale endtime sale.endTime
. If the auction has not completed yet, it will fail and never finish since the transaction will revert when calling getPrice()
function which underflows. All funds are stuck in the contract.
In the following test, the buy on the Day 6 will revert due to sale.price
calculation underflow. Funds are stuck in the contract.
// 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 LPDAPriceFastDropTest is EscherTest { LPDAFactory public lpdaSales; LPDA.Sale public lpdaSale; LPDA public sale; function test_PriceFastDrop_LPDA() public { 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) / 5 days), startTime: uint96(block.timestamp), saleReceiver: payable(address(69)), endTime: uint96(block.timestamp + 10 days) }); // make the lpda sales contract sale = LPDA(lpdaSales.createLPDASale(lpdaSale)); // authorize the lpda sale to mint tokens edition.grantRole(edition.MINTER_ROLE(), address(sale)); // immediatly buying 1 NFT succeed sale.buy{value: 5 ether}(5); assertEq(address(sale).balance, 5 ether); // price will drop to negative in 6 days; transaction will revert vm.warp(block.timestamp + 6 days); vm.expectRevert(); sale.buy{value: uint256(1 ether)}(1); } }
manual audit.
Setting parameters to guarantee sale.dropPerSecond < sale.startPrice/(sale.endTime - sale.startTime)
.
#0 - c4-judge
2022-12-11T11:38:00Z
berndartmueller marked the issue as duplicate of #392
#1 - c4-judge
2023-01-02T19:54:41Z
berndartmueller marked the issue as satisfactory
🌟 Selected for report: AkshaySrivastav
Also found by: 0x52, 0xA5DF, 0xdeadbeef0x, KingNFT, Madalad, Parth, Soosh, _Adam, adriro, csanuragjain, danyams, eyexploit, gasperpre, gz627, gzeon, hansfriese, hihen, immeas, jadezti, jonatascm, kiki_dev, kree-dotcom, ladboy233, lukris02, lumoswiz, mahdikarimi, minhtrng, nalus, nameruse, obront, reassor, rvierdiiev, seyni, tnevler, wait, yixxas
1.3417 USDC - $1.34
https://github.com/code-423n4/2022-12-escher/blob/main/src/minters/LPDA.sol#L81
Under certain situations LPDA
auction may never sell out all items. The sales income and fees have to leave in the contract.
With current coding logic, a normal LPDA
auction can only be completed by selling out all items. However, in some situations, this may not happen, e.g. people are no longer interested in those NFTs. In these situations, artists cannot get the sales income; the feeReceiver
cannot get the fee.
manual audit
To implement a mechanism to permit artists to end the auction after a pre-specified time period if the auction has not completed yet.
#0 - c4-judge
2022-12-12T09:01:32Z
berndartmueller marked the issue as duplicate of #328
#1 - c4-judge
2023-01-02T20:22:49Z
berndartmueller marked the issue as satisfactory