Product Marketplace
The xendune marketplace supports four product types, each with dedicated escrow templates deployed via ProductFactory using EIP-1167 clones. Up to 24 products per page (MAX_PAGE_SIZE = 24).
Product Types
Physical Products (PhysicalProductTemplate) — 3% fee
For tangible goods requiring logistics delivery. Escrow flow includes:
- Buyer places order → USDT locked in contract
- Seller ships goods and enters tracking number
- Auto-confirm countdown begins (duration configured by seller)
- Buyer confirms receipt or countdown expires → settlement
- Either party can request community arbitration after shipping
Virtual Products (VirtualProductTemplate) — 3% fee
For digital goods (game keys, software licenses, digital content, etc.). Similar to physical products but with these differences:
- Seller confirms digital delivery completion on-chain
- Auto-confirm countdown begins after delivery confirmation
- No logistics tracking
Services (ServiceProductTemplate) — 5% fee
For local offline services. Key differences:
- Geolocation-based: product tied to a specific city (language/keyword/country/province/city), indexed via ServiceLocationIndex
- Uses service items instead of specs (name + individual price)
- Buyer confirms service completion to trigger settlement
- No auto-confirm mechanism—buyer must actively confirm
WantToBuy (WantToBuyTemplate) — 3% fee
Buyer publishes a demand; seller responds and fulfills:
- Buyer (publisher) creates a WantToBuy order and locks USDT
- Seller (acceptor) accepts the order and begins fulfillment
- Buyer confirms receipt → settlement to seller
Creating Products
Products are created through ProductFactory:
- Keywords must be pre-approved by admin/customer service
- Seller must not be blacklisted
- Active product limit: no deposit — max 1; with deposit — max 5 (
maxProductsWithoutDeposit / maxProductsWithDeposit, adjustable by Owner)
The factory clones the corresponding template, calls initialize(), and registers the product in all indexes.
Multi-Order Support
Each product contract supports multiple concurrent orders. Orders are identified by unique bytes16 orderId generated as:
keccak256(abi.encodePacked(contractAddress, buyer, timestamp, nonce))
This enables a single product listing to handle multiple buyers simultaneously without deploying new contracts per transaction.
Order Lifecycle
Physical/Virtual Products
Placed → Shipped/Delivered → Completed
↓ ↓
Cancelled In Arbitration → Resolved
Services
Placed → Service In Progress → Completed
↓ ↓
Cancelled In Arbitration → Resolved
Specs and Pricing
Physical and virtual products use Spec structs:
name: specification name (e.g., "64GB", "Premium Edition")price: USDT price with decimalsstock: available inventory
Service products use ServiceItem:
name: service nameprice: USDT price
Sellers can update specs/service items and product info when no orders are in progress.
Auto-Confirm
Physical and virtual products have auto-confirm mechanisms:
- Deadline set after shipping/delivery
- If buyer doesn't confirm receipt before deadline, anyone can call
triggerAutoReceive()to complete the order - Seller can extend deadline when needed (e.g., logistics delays)
- This mechanism prevents funds from being locked indefinitely
Product Delisting
Products can be delisted (removed from active listings) three ways:
- Seller voluntary delisting:
delistProduct()— requires no orders in progress - Admin forced delisting:
adminDelistProduct()— requires no orders in progress - Factory forced delisting:
delistByFactory()— called during zombie deposit reclamation
After delisting, the product contract remains on-chain for historical queries but no longer accepts new orders.
Settlement
When an order completes, fees are distributed to the seller's invite chain via InviteRegistry (L1 gets 20%, L2 gets 10% of the fee), with the remainder split to the platform via PlatformFeeSplitter. The net amount is then transferred to the seller. If any transfer fails, the amount is stored in pendingWithdrawals[recipient] for later claim via claimPending().