Omniscia Seen Haus Audit
SaleRunnerFacet Manual Review Findings
SaleRunnerFacet Manual Review Findings
SRF-01M: Inexistent Validation of Remaining Supply
Type | Severity | Location |
---|---|---|
Logical Fault | Medium | SaleRunnerFacet.sol:L105, L134 |
Description:
The ticket mechanism of the sale runner does not validate the specified purchase amount can actually be fulfilled by the consignment.
Example:
contracts/market/handlers/facets/SaleRunnerFacet.sol
95function buy(uint256 _consignmentId, uint256 _amount)96external97override98payable99onlyAudienceMember(_consignmentId)100{101 // Get Market Handler Storage slot102 MarketHandlerLib.MarketHandlerStorage storage mhs = MarketHandlerLib.marketHandlerStorage();103
104 // Get the consignment105 Consignment memory consignment = getMarketController().getConsignment(_consignmentId);106
107 // Make sure the sale exists108 Sale storage sale = mhs.sales[_consignmentId];109 require(sale.start != 0, "Sale does not exist");110
111 // Make sure we can accept the buy order112 require(block.timestamp >= sale.start, "Sale hasn't started");113 require(!AddressUpgradeable.isContract(msg.sender), "Contracts may not buy");114 require(_amount <= sale.perTxCap, "Per transaction limit for this sale exceeded");115 require(msg.value == sale.price * _amount, "Payment does not cover order price");116
117 // If this was the first successful purchase...118 if (sale.state == State.Pending) {119
120 // First buy updates sale state to Running121 sale.state = State.Running;122
123 // Notify listeners of state change124 emit SaleStarted(_consignmentId);125
126 }127
128 // Determine if consignment is physical129 address nft = getMarketController().getNft();130 if (nft == consignment.tokenAddress && ISeenHausNFT(nft).isPhysical(consignment.tokenId)) {131
132 // Issue an escrow ticket to the buyer133 address escrowTicketer = getMarketController().getEscrowTicketer(_consignmentId);134 IEscrowTicketer(escrowTicketer).issueTicket(_consignmentId, _amount, payable(msg.sender));135
136 } else {137
138 // Release the purchased amount of the consigned token supply to buyer139 getMarketController().releaseConsignment(_consignmentId, _amount, msg.sender);140
141 }142
143 uint256 pendingPayoutValue = consignment.pendingPayout + msg.value;144 getMarketController().setConsignmentPendingPayout(consignment.id, pendingPayoutValue);145
146 // Announce the purchase147 emit Purchase(consignment.id, msg.sender, _amount, msg.value);148
149 // Track the sale info against the token itself150 emit TokenHistoryTracker(consignment.tokenAddress, consignment.tokenId, msg.sender, msg.value, _amount, consignment.id);151}
Recommendation:
We advise this trait of the system to be evaluated and proper validation to be imposed as otherwise unfulfill-able tickets may be created.
Alleviation:
The code within the issueTicket
and releaseConsignment
functions was updated instead, thereby alleviating this exhibit.