Marketplace Architecture

The secondary marketplace is a hybrid: USDC is escrowed and settled on-chain by a dedicated Anchor program, while skin ownership stays on a fast off-chain ledger that the server re-keys when settlement confirms.

Components

ComponentRole
Marketplace module + home buttonThe new in-game UI: listings, bids, Buy-Now.
ions_market (Anchor program)Escrows USDC and is the sole fund authority. Settles sales, runs the fee split, refunds losers.
Cloudflare cron keeperDetects USDC deposits, triggers the program instructions. Cannot drain — the PDA holds the funds.
Off-chain ledger (owned_skins)Source of truth for who owns which edition. Re-keyed on confirmed settlement.
PumpSwap (pump.fun AMM)Where the 4.8% buyback leg swaps USDC → $IONS before burning.

Settlement flow (Buy-Now)

Buyer sends exact USDC ─► ions_market escrow (PDA)
        │
        ▼
  keeper detects deposit ─► triggers settle()
        │
        ├─ 90.4% USDC ─► verified seller wallet
        ├─  4.8% USDC ─► PumpSwap: buy $IONS ─► burn      (buyback & burn)
        └─  4.8% USDC ─► treasury
        │
        ▼
  on confirmation ─► server re-keys owned_skins (edition → buyer)

Ownership = off-chain, editioned

Ownership lives in the owned_skins ledger with an edition column so the primary key becomes (price, edition) — each of a skin's 100 editions is individually owned and individually tradeable. On-chain NFTs (freeze-soulbound) are a shelved future upgrade; the current design keeps ownership off-chain to stay compatible with the no-wallet-connect rail.

Fund-safety invariants (must-fix-or-don't-ship)

From the fund-safety design pass — these are required before the resale market goes live:

  • BigInt amounts with ceil-with-remainder fee math (no rounding the fee to zero).
  • Server-side minimum-ask floor so dust self-asks can't dodge the fee.
  • Two-phase owed/paid payout with signature-keyed idempotency for escrow↔ledger atomicity (a payout is recorded as owed, then paid, keyed by tx signature so it can't double-pay).
  • Single-flighted keeper — never two keeper runs racing the same deposit.
  • Capped settlement float and payout destination bound to the verified seller wallet — the program can only pay the seller who actually listed.

Deploy note

ions_market shares the same deploy wall as the existing ions_skins program (rustc ≥ 1.85 / edition2024). The build approach: ship the UI shell (module + home button) first, then write and validate the ions_market program + server/keeper, and deploy later.

Trust boundary: the keeper is a convenience that triggers settlement; it is never a custodian. All funds sit in the program PDA, and only the program's hardcoded logic can move them — to the seller, to the buyback, or to the treasury.