Omniscia Seen Haus Audit

SaleRunnerFacet Manual Review Findings

SaleRunnerFacet Manual Review Findings

SRF-01M: Inexistent Validation of Remaining Supply

TypeSeverityLocation
Logical FaultMediumSaleRunnerFacet.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)
96external
97override
98payable
99onlyAudienceMember(_consignmentId)
100{
101 // Get Market Handler Storage slot
102 MarketHandlerLib.MarketHandlerStorage storage mhs = MarketHandlerLib.marketHandlerStorage();
103
104 // Get the consignment
105 Consignment memory consignment = getMarketController().getConsignment(_consignmentId);
106
107 // Make sure the sale exists
108 Sale storage sale = mhs.sales[_consignmentId];
109 require(sale.start != 0, "Sale does not exist");
110
111 // Make sure we can accept the buy order
112 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 Running
121 sale.state = State.Running;
122
123 // Notify listeners of state change
124 emit SaleStarted(_consignmentId);
125
126 }
127
128 // Determine if consignment is physical
129 address nft = getMarketController().getNft();
130 if (nft == consignment.tokenAddress && ISeenHausNFT(nft).isPhysical(consignment.tokenId)) {
131
132 // Issue an escrow ticket to the buyer
133 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 buyer
139 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 purchase
147 emit Purchase(consignment.id, msg.sender, _amount, msg.value);
148
149 // Track the sale info against the token itself
150 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.