1+
2+ //! # Commitment Marketplace Contract
3+ //!
4+ //! Soroban smart contract for NFT marketplace operations (listings, offers, auctions) with reentrancy guard and fee logic.
5+ //!
6+ //! ## Security
7+ //! - All state-changing entry points require authentication (`require_auth`).
8+ //! - Reentrancy guard is enforced on all external-call entry points.
9+ //! - Arithmetic is performed using checked math; see individual functions for overflow/underflow notes.
10+ //!
11+ //! ## Errors
12+ //! - See [`MarketplaceError`] for all error codes.
13+ //!
14+ //! ## Storage
15+ //!
16+ //! - See [`DataKey`] for all storage keys mutated by each entry point.
17+ //!
18+ //! ## Audit Notes
19+ //! - No cross-contract NFT ownership checks are performed in this implementation (see comments in code).
20+ //! - All token transfers use Soroban token interface.
21+
122#![ no_std]
223
324use soroban_sdk:: {
@@ -140,13 +161,14 @@ impl CommitmentMarketplace {
140161 // Initialization
141162 // ========================================================================
142163
143- /// Initialize the marketplace
144- ///
145- /// # Arguments
146- /// * `admin` - Admin address
147- /// * `nft_contract` - Address of the CommitmentNFT contract
148- /// * `fee_basis_points` - Marketplace fee in basis points (e.g., 250 = 2.5%)
149- /// * `fee_recipient` - Address to receive marketplace fees
164+ /// @notice Initialize the marketplace contract.
165+ /// @param admin Admin address (must sign the transaction).
166+ /// @param nft_contract Address of the CommitmentNFT contract.
167+ /// @param fee_basis_points Marketplace fee in basis points (e.g., 250 = 2.5%).
168+ /// @param fee_recipient Address to receive marketplace fees.
169+ /// @dev Only callable once. Sets up admin, NFT contract, fee, and fee recipient.
170+ /// @error MarketplaceError::AlreadyInitialized if already initialized.
171+ /// @security Only callable by `admin` (require_auth).
150172 pub fn initialize (
151173 e : Env ,
152174 admin : Address ,
@@ -184,15 +206,21 @@ impl CommitmentMarketplace {
184206 Ok ( ( ) )
185207 }
186208
187- /// Get admin address
209+ /// @notice Get the admin address for the marketplace.
210+ /// @return admin Address of the admin.
211+ /// @error MarketplaceError::NotInitialized if not initialized.
188212 pub fn get_admin ( e : Env ) -> Result < Address , MarketplaceError > {
189213 e. storage ( )
190214 . instance ( )
191215 . get ( & DataKey :: Admin )
192216 . ok_or ( MarketplaceError :: NotInitialized )
193217 }
194218
195- /// Update marketplace fee (admin only)
219+ /// @notice Update the marketplace fee (basis points).
220+ /// @param fee_basis_points New fee in basis points.
221+ /// @dev Only callable by admin.
222+ /// @error MarketplaceError::NotInitialized if not initialized.
223+ /// @security Only callable by `admin` (require_auth).
196224 pub fn update_fee ( e : Env , fee_basis_points : u32 ) -> Result < ( ) , MarketplaceError > {
197225 let admin: Address = Self :: get_admin ( e. clone ( ) ) ?;
198226 admin. require_auth ( ) ;
@@ -211,16 +239,16 @@ impl CommitmentMarketplace {
211239 // Listing Management
212240 // ========================================================================
213241
214- /// List an NFT for sale
215- ///
216- /// # Arguments
217- /// * `seller` - The seller's address (must be NFT owner)
218- /// * `token_id` - The NFT token ID to list
219- /// * `price` - The sale price
220- /// * `payment_token` - The token contract address for payment
221- ///
222- /// # Reentrancy Protection
223- /// Protected with reentrancy guard as it makes external NFT contract calls
242+ /// @notice List an NFT for sale on the marketplace.
243+ /// @param seller Seller's address (must be NFT owner and sign the transaction).
244+ /// @param token_id NFT token ID to list.
245+ /// @param price Sale price (must be > 0).
246+ /// @param payment_token Token contract address for payment.
247+ /// @dev Reentrancy guard enforced. No cross-contract NFT ownership check in this implementation.
248+ /// @error MarketplaceError::InvalidPrice if price <= 0.
249+ /// @error MarketplaceError::ListingExists if listing already exists.
250+ /// @error MarketplaceError::NotInitialized if contract not initialized.
251+ /// @security Only callable by `seller` (require_auth).
224252 pub fn list_nft (
225253 e : Env ,
226254 seller : Address ,
@@ -311,10 +339,13 @@ impl CommitmentMarketplace {
311339 Ok ( ( ) )
312340 }
313341
314- /// Cancel a listing
315- ///
316- /// # Reentrancy Protection
317- /// Uses checks-effects-interactions pattern
342+ /// @notice Cancel an active NFT listing.
343+ /// @param seller Seller's address (must sign the transaction).
344+ /// @param token_id NFT token ID to cancel listing for.
345+ /// @dev Reentrancy guard enforced. Checks-effects-interactions pattern.
346+ /// @error MarketplaceError::ListingNotFound if listing does not exist.
347+ /// @error MarketplaceError::NotSeller if caller is not the seller.
348+ /// @security Only callable by `seller` (require_auth).
318349 pub fn cancel_listing ( e : Env , seller : Address , token_id : u32 ) -> Result < ( ) , MarketplaceError > {
319350 // Reentrancy protection
320351 let guard: bool = e
@@ -377,14 +408,14 @@ impl CommitmentMarketplace {
377408 Ok ( ( ) )
378409 }
379410
380- /// Buy an NFT
381- ///
382- /// # Arguments
383- /// * `buyer` - The buyer's address
384- /// * `token_id` - The NFT token ID to buy
385- ///
386- /// # Reentrancy Protection
387- /// Critical - handles token transfers. Protected with reentrancy guard .
411+ /// @notice Buy an NFT from an active listing.
412+ /// @param buyer Buyer's address (must sign the transaction).
413+ /// @param token_id NFT token ID to buy.
414+ /// @dev Reentrancy guard enforced. Handles token transfers. No cross-contract NFT transfer in this implementation.
415+ /// @error MarketplaceError::ListingNotFound if listing does not exist.
416+ /// @error MarketplaceError::CannotBuyOwnListing if buyer is seller.
417+ /// @error MarketplaceError::NotInitialized if contract not initialized.
418+ /// @security Only callable by `buyer` (require_auth) .
388419 pub fn buy_nft ( e : Env , buyer : Address , token_id : u32 ) -> Result < ( ) , MarketplaceError > {
389420 // Reentrancy protection
390421 let guard: bool = e
@@ -497,15 +528,19 @@ impl CommitmentMarketplace {
497528 Ok ( ( ) )
498529 }
499530
500- /// Get a listing
531+ /// @notice Get details of a specific NFT listing.
532+ /// @param token_id NFT token ID.
533+ /// @return Listing struct.
534+ /// @error MarketplaceError::ListingNotFound if listing does not exist.
501535 pub fn get_listing ( e : Env , token_id : u32 ) -> Result < Listing , MarketplaceError > {
502536 e. storage ( )
503537 . persistent ( )
504538 . get ( & DataKey :: Listing ( token_id) )
505539 . ok_or ( MarketplaceError :: ListingNotFound )
506540 }
507541
508- /// Get all active listings
542+ /// @notice Get all active NFT listings.
543+ /// @return Vec<Listing> of all active listings.
509544 pub fn get_all_listings ( e : Env ) -> Vec < Listing > {
510545 let active_listings: Vec < u32 > = e
511546 . storage ( )
@@ -532,10 +567,15 @@ impl CommitmentMarketplace {
532567 // Offer System
533568 // ========================================================================
534569
535- /// Make an offer on an NFT
536- ///
537- /// # Reentrancy Protection
538- /// Protected with reentrancy guard
570+ /// @notice Make an offer on an NFT.
571+ /// @param offerer Offer maker's address (must sign the transaction).
572+ /// @param token_id NFT token ID to make offer on.
573+ /// @param amount Offer amount (must be > 0).
574+ /// @param payment_token Token contract address for payment.
575+ /// @dev Reentrancy guard enforced.
576+ /// @error MarketplaceError::InvalidOfferAmount if amount <= 0.
577+ /// @error MarketplaceError::OfferExists if offerer already has an offer.
578+ /// @security Only callable by `offerer` (require_auth).
539579 pub fn make_offer (
540580 e : Env ,
541581 offerer : Address ,
@@ -608,10 +648,14 @@ impl CommitmentMarketplace {
608648 Ok ( ( ) )
609649 }
610650
611- /// Accept an offer
612- ///
613- /// # Reentrancy Protection
614- /// Critical - handles token transfers. Protected with reentrancy guard.
651+ /// @notice Accept an offer on an NFT.
652+ /// @param seller Seller's address (must sign the transaction).
653+ /// @param token_id NFT token ID.
654+ /// @param offerer Address of the offer maker.
655+ /// @dev Reentrancy guard enforced. Handles token transfers. No cross-contract NFT transfer in this implementation.
656+ /// @error MarketplaceError::OfferNotFound if offer does not exist.
657+ /// @error MarketplaceError::NotInitialized if contract not initialized.
658+ /// @security Only callable by `seller` (require_auth).
615659 pub fn accept_offer (
616660 e : Env ,
617661 seller : Address ,
@@ -724,7 +768,11 @@ impl CommitmentMarketplace {
724768 Ok ( ( ) )
725769 }
726770
727- /// Cancel an offer
771+ /// @notice Cancel an offer made on an NFT.
772+ /// @param offerer Offer maker's address (must sign the transaction).
773+ /// @param token_id NFT token ID.
774+ /// @error MarketplaceError::OfferNotFound if offer does not exist.
775+ /// @security Only callable by `offerer` (require_auth).
728776 pub fn cancel_offer ( e : Env , offerer : Address , token_id : u32 ) -> Result < ( ) , MarketplaceError > {
729777 offerer. require_auth ( ) ;
730778
@@ -755,7 +803,9 @@ impl CommitmentMarketplace {
755803 Ok ( ( ) )
756804 }
757805
758- /// Get all offers for a token
806+ /// @notice Get all offers for a specific NFT token.
807+ /// @param token_id NFT token ID.
808+ /// @return Vec<Offer> of all offers for the token.
759809 pub fn get_offers ( e : Env , token_id : u32 ) -> Vec < Offer > {
760810 e. storage ( )
761811 . persistent ( )
@@ -767,10 +817,17 @@ impl CommitmentMarketplace {
767817 // Auction System
768818 // ========================================================================
769819
770- /// Start an auction
771- ///
772- /// # Reentrancy Protection
773- /// Protected with reentrancy guard
820+ /// @notice Start an auction for an NFT.
821+ /// @param seller Seller's address (must sign the transaction).
822+ /// @param token_id NFT token ID.
823+ /// @param starting_price Starting price for the auction (must be > 0).
824+ /// @param duration_seconds Duration of the auction in seconds (must be > 0).
825+ /// @param payment_token Token contract address for payment.
826+ /// @dev Reentrancy guard enforced.
827+ /// @error MarketplaceError::InvalidPrice if starting price <= 0.
828+ /// @error MarketplaceError::InvalidDuration if duration is 0.
829+ /// @error MarketplaceError::ListingExists if auction already exists for token.
830+ /// @security Only callable by `seller` (require_auth).
774831 pub fn start_auction (
775832 e : Env ,
776833 seller : Address ,
@@ -858,10 +915,15 @@ impl CommitmentMarketplace {
858915 Ok ( ( ) )
859916 }
860917
861- /// Place a bid
862- ///
863- /// # Reentrancy Protection
864- /// Critical - handles token transfers for bid refunds. Protected with reentrancy guard.
918+ /// @notice Place a bid on an active auction.
919+ /// @param bidder Bidder's address (must sign the transaction).
920+ /// @param token_id NFT token ID.
921+ /// @param bid_amount Amount of the bid (must be > current bid).
922+ /// @dev Reentrancy guard enforced. Handles token transfers for bid refunds.
923+ /// @error MarketplaceError::AuctionEnded if auction has ended.
924+ /// @error MarketplaceError::BidTooLow if bid is not higher than current bid.
925+ /// @error MarketplaceError::CannotBuyOwnListing if seller tries to bid.
926+ /// @security Only callable by `bidder` (require_auth).
865927 pub fn place_bid (
866928 e : Env ,
867929 bidder : Address ,
@@ -953,10 +1015,12 @@ impl CommitmentMarketplace {
9531015 Ok ( ( ) )
9541016 }
9551017
956- /// End an auction
957- ///
958- /// # Reentrancy Protection
959- /// Critical - handles final settlement. Protected with reentrancy guard.
1018+ /// @notice End an auction and settle payment/NFT transfer.
1019+ /// @param token_id NFT token ID.
1020+ /// @dev Reentrancy guard enforced. Handles final settlement. Anyone can call after auction ends.
1021+ /// @error MarketplaceError::AuctionNotFound if auction does not exist.
1022+ /// @error MarketplaceError::AuctionNotEnded if auction has not ended yet.
1023+ /// @error MarketplaceError::AuctionEnded if auction already ended.
9601024 pub fn end_auction ( e : Env , token_id : u32 ) -> Result < ( ) , MarketplaceError > {
9611025 // Reentrancy protection
9621026 let guard: bool = e
@@ -1084,15 +1148,19 @@ impl CommitmentMarketplace {
10841148 Ok ( ( ) )
10851149 }
10861150
1087- /// Get auction details
1151+ /// @notice Get details of a specific auction.
1152+ /// @param token_id NFT token ID.
1153+ /// @return Auction struct.
1154+ /// @error MarketplaceError::AuctionNotFound if auction does not exist.
10881155 pub fn get_auction ( e : Env , token_id : u32 ) -> Result < Auction , MarketplaceError > {
10891156 e. storage ( )
10901157 . persistent ( )
10911158 . get ( & DataKey :: Auction ( token_id) )
10921159 . ok_or ( MarketplaceError :: AuctionNotFound )
10931160 }
10941161
1095- /// Get all active auctions
1162+ /// @notice Get all active auctions.
1163+ /// @return Vec<Auction> of all active auctions.
10961164 pub fn get_all_auctions ( e : Env ) -> Vec < Auction > {
10971165 let active_auctions: Vec < u32 > = e
10981166 . storage ( )
0 commit comments