Skip to content

Marketplace Module

The marketplace module provides an escrow-based marketplace for trading RWA tokens using SUI. It ensures secure, atomic swaps with automatic fee collection and change return.

Overview

The marketplace is a shared object that holds all listings and manages the escrow system. Sellers can list tokens for sale, update prices, cancel listings, and buyers can purchase tokens with automatic token and SUI transfers.

Key Structures

Marketplace

Central shared object holding all listings:

public struct Marketplace has key {
    id: UID,
    listings: Table<ID, Listing>,    // Maps listing ID to listing
    fee_basis_points: u64,           // Fee as basis points (e.g., 50 = 0.5%)
    total_volume: u64,               // Cumulative trading volume (MIST)
}

Listing

Individual token listing:

public struct Listing has store {
    token: RwaToken,                 // The token being sold
    seller: address,                 // Seller address
    price: u64,                      // Price in MIST (1 SUI = 1,000,000,000 MIST)
    created_at: u64,                 // Listing creation timestamp
}

Main Functions

list()

List a token for sale (moves token to escrow):

public entry fun list(
    marketplace: &mut Marketplace,
    token: RwaToken,
    price: u64,
    ctx: &mut TxContext
): ID

Parameters:

  • marketplace: Mutable reference to Marketplace
  • token: RWA token to list
  • price: Price in MIST
  • ctx: Transaction context

Returns: Listing ID

Effects:

  • Moves token to marketplace escrow
  • Creates listing record
  • Emits ListingCreated event

update_price()

Update listing price (seller only):

public entry fun update_price(
    marketplace: &mut Marketplace,
    listing_id: ID,
    new_price: u64,
    ctx: &TxContext
)

Requirements:

  • Caller must be the seller
  • Listing must exist and be active

Effects:

  • Updates listing price
  • Emits ListingUpdated event

cancel()

Cancel listing and return token to seller:

public entry fun cancel(
    marketplace: &mut Marketplace,
    listing_id: ID,
    ctx: &TxContext
): RwaToken

Requirements:

  • Caller must be the seller
  • Listing must exist

Effects:

  • Removes listing
  • Returns token to seller
  • Emits ListingCancelled event

buy()

Purchase listed token with SUI payment:

public entry fun buy(
    marketplace: &mut Marketplace,
    listing_id: ID,
    payment: Coin<SUI>,
    ctx: &mut TxContext
): (RwaToken, Coin<SUI>)

Parameters:

  • marketplace: Mutable reference to Marketplace
  • listing_id: ID of listing to purchase
  • payment: Coin object containing SUI payment
  • ctx: Transaction context

Returns:

  • RwaToken: The purchased token
  • Coin<SUI>: Change (if payment exceeded price)

Effects:

  • Transfers token to buyer
  • Calculates fee and seller proceeds
  • Transfers SUI to seller (minus fee)
  • Returns change to buyer (if overpaid)
  • Updates total volume
  • Removes listing
  • Emits ListingPurchased event

Events

ListingCreated

public struct ListingCreated has copy, drop {
    listing_id: ID,
    token_id: ID,
    seller: address,
    price: u64,
    timestamp: u64,
}

ListingUpdated

public struct ListingUpdated has copy, drop {
    listing_id: ID,
    new_price: u64,
    timestamp: u64,
}

ListingCancelled

public struct ListingCancelled has copy, drop {
    listing_id: ID,
    seller: address,
    timestamp: u64,
}

ListingPurchased

public struct ListingPurchased has copy, drop {
    listing_id: ID,
    token_id: ID,
    seller: address,
    buyer: address,
    price: u64,
    fee: u64,
    timestamp: u64,
}

Fee System

Fees are calculated as a percentage of the sale price:

fee = (price * fee_basis_points) / 10000
seller_proceeds = price - fee

Example:

  • Price: 100 SUI (100,000,000,000 MIST)
  • Fee: 0.5% (50 basis points)
  • Fee Amount: 0.5 SUI (500,000,000 MIST)
  • Seller Receives: 99.5 SUI

Usage Examples

List Token

sui client call \
  --package <PACKAGE_ID> \
  --module marketplace \
  --function list \
  --args <MARKETPLACE_ID> <TOKEN_ID> 1000000000 \
  --gas-budget 10000000

Update Price

sui client call \
  --package <PACKAGE_ID> \
  --module marketplace \
  --function update_price \
  --args <MARKETPLACE_ID> <LISTING_ID> 1500000000 \
  --gas-budget 10000000

Buy Token

sui client call \
  --package <PACKAGE_ID> \
  --module marketplace \
  --function buy \
  --args <MARKETPLACE_ID> <LISTING_ID> <COIN_ID> \
  --gas-budget 10000000

TypeScript Example

const tx = new TransactionBlock();
 
// Get payment coin
const payment = tx.splitCoins(tx.gas, [tx.pure.u64(priceInMist)]);
 
// Buy token
const [token, change] = tx.moveCall({
  target: `${PACKAGE_ID}::marketplace::buy`,
  arguments: [
    tx.object(MARKETPLACE_ID),
    tx.pure.id(listingId),
    payment,
  ],
  typeArguments: [],
});
 
tx.transferObjects([token, change], tx.pure.address(buyerAddress));
 
await suiClient.signAndExecuteTransactionBlock({
  signer: keypair,
  transactionBlock: tx,
});

Features

Escrow Protection

  • Tokens are locked in marketplace during listing
  • Cannot be transferred or used elsewhere
  • Seller can cancel to retrieve token

Automatic Change Return

  • Buyers receive change if payment exceeds price
  • Prevents overpayment losses
  • Handled atomically in transaction

Overpayment Handling

If buyer pays more than listing price:

  1. Token transferred to buyer
  2. Full price (minus fee) sent to seller
  3. Change returned to buyer

Volume Tracking

  • Cumulative trading volume stored on marketplace
  • Can be queried for statistics
  • Updated on each purchase

Security Considerations

Shared Object Safety

  • Marketplace is a shared object (concurrent access safe)
  • Table provides efficient key-value storage
  • No race conditions in listing/buying

Price Validation

  • Prices must be positive
  • Payment must cover at least the listing price
  • Change calculation prevents rounding errors

Access Control

  • Only seller can update/cancel their listing
  • Buyers must provide sufficient payment
  • Fees are automatically collected

Integration with Document Access

When a token is purchased:

  1. Token ownership transfers to buyer
  2. Buyer becomes the new owner
  3. Access keys become available to new owner
  4. Previous owner loses access

This ensures document access follows token ownership.

Best Practices

  1. Price Setting: Set competitive but fair prices
  2. Gas Considerations: Include sufficient gas budget
  3. Cancellation: Cancel listings you no longer want to sell
  4. Change Handling: Check returned change coins

Next Steps