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: 68/119
Findings: 1
Award: $35.02
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: slvDev
Also found by: 0x4non, Bnke0x0, Diana, Dinesh11G, RaymondFam, ReyAdmirado, adriro, ahmedov, ajtra, c3phas, cryptostellar5, nicobevi, pfapostol, sakshamguruji, tnevler, zaskoh
35.0246 USDC - $35.02
/// @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 >= sale_.startTime, "TOO SOON"); require(_amount * sale_.price == msg.value, "WRONG PRICE"); uint48 newId = uint48(_amount) + sale_.currentId; require(newId <= sale_.finalId, "TOO MANY"); for (uint48 x = sale_.currentId + 1; x <= newId; x++) { nft.mint(msg.sender, x); } sale.currentId = newId; emit Buy(msg.sender, _amount, msg.value, sale); if (newId == sale_.finalId) _end(sale); }
Replace the Sale memory sale
definition and just copy sale.currentId
to memory.
function buy(uint256 _amount) external payable { IEscher721 nft = IEscher721(sale.edition); require(block.timestamp >= sale.startTime, "TOO SOON"); require(_amount * sale.price == msg.value, "WRONG PRICE"); uint48 currentId_ = sale.currentId; uint48 newId = uint48(_amount) + currentId_; require(newId <= sale.finalId, "TOO MANY"); for (uint48 x = currentId_ + 1; x <= newId; x++) { nft.mint(msg.sender, x); } sale.currentId = newId; emit Buy(msg.sender, _amount, msg.value, sale); if (newId == sale.finalId) _end(sale); }
_end()
callSince there is just one sale and after it's ended the contract is selfdestructed, sale can be referenced from storage. Making unnecesary a sale parameter on _end()
definition.
function _end(Sale memory _sale) internal { emit End(_sale); ISaleFactory(factory).feeReceiver().transfer(address(this).balance / 20); selfdestruct(_sale.saleReceiver); }
Replace the code with
function _end() internal { Sale memory _sale = sale; ISaleFactory(factory).feeReceiver().transfer(address(this).balance / 20); selfdestruct(_sale.saleReceiver); emit End(_sale); }
sale.currentId
and sale.finalId
in memory instead of the entire sale struct on buy()
on LPDA.sol
to save gas.function buy(uint256 _amount) external payable { uint48 amount = uint48(_amount); // Sale memory temp = sale; // Remove this IEscher721 nft = IEscher721(sale.edition); require(block.timestamp >= sale.startTime, "TOO SOON"); uint256 price = getPrice(); require(msg.value >= amount * price, "WRONG PRICE"); // Cache only this two values uint48 currentId_ = sale.currentId; uint48 finalId_ = sale.finalId; amountSold += amount; uint48 newId = amount + currentId_; require(newId <= finalId_, "TOO MANY"); receipts[msg.sender].amount += amount; receipts[msg.sender].balance += uint80(msg.value); for (uint256 x = currentId_ + 1; x <= newId; x++) { nft.mint(msg.sender, x); } sale.currentId = newId; emit Buy(msg.sender, amount, msg.value, sale); if (newId == finalId_) { sale.finalPrice = uint80(price); uint256 totalSale = price * amountSold; uint256 fee = totalSale / 20; ISaleFactory(factory).feeReceiver().transfer(fee); sale.saleReceiver.transfer(totalSale - fee); _end(); } }
amountSold
could be replaced with sale.currentId + 1
on LPDA.sol
.Replace
// uint48 public amountSold = 0; // Replace this with a view function function amountSold() public view returns (uint48) { return sale.currentId; }
// LPDA.sol Line 66 // amountSold += amount; // Comment this line // LPDA.sol Line 83 // uint256 totalSale = price * amountSold; Change this line with uint256 totalSale = price * amountSold();
storage
modifier will save some gas.// Instead of this // receipts[msg.sender].amount += amount; // receipts[msg.sender].balance += uint80(msg.value); // Do this Receipt storage receipt = receipts[msg.sender]; receipt.amount += amount; receipt.balance += uint80(msg.value);
// Replace this // for (uint256 x = temp.currentId + 1; x <= newId; x++) { // nft.mint(msg.sender, x); // } // With this for (uint256 x = temp.currentId + 1; x <= newId;) { nft.mint(msg.sender, x); unchecked { ++x; } }
// Replace this // for (uint48 x = sale_.currentId + 1; x <= newId; x++) { // nft.mint(msg.sender, x); // } // With this for (uint48 x = sale_.currentId + 1; x <= newId;) { nft.mint(msg.sender, x); unchecked { ++x; } }
// Replace this for (uint24 x = temp.currentId + 1; x <= newId; x++) { nft.mint(msg.sender, x); } // With this for (uint24 x = temp.currentId + 1; x <= newId;) { nft.mint(msg.sender, x); unchecked { ++x; } }
temp.price
instead of sale.price
on buy()
function buy(uint256 _amount) external payable { uint24 amount = uint24(_amount); Sale memory temp = sale; IEscher721 nft = IEscher721(temp.edition); require(block.timestamp >= temp.startTime, "TOO SOON"); require(block.timestamp < temp.endTime, "TOO LATE"); // require(amount * sale.price == msg.value, "WRONG PRICE"); // Replace this line with the line below require(amount * temp.price == msg.value, "WRONG PRICE"); uint24 newId = amount + temp.currentId; for (uint24 x = temp.currentId + 1; x <= newId; x++) { nft.mint(msg.sender, x); } sale.currentId = newId; emit Buy(msg.sender, amount, msg.value, temp); }
#0 - c4-sponsor
2022-12-22T22:57:04Z
mehtaculous marked the issue as sponsor disputed
#1 - c4-judge
2023-01-04T11:03:50Z
berndartmueller marked the issue as grade-b