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
| Component | Role |
|---|---|
| Marketplace module + home button | The 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 keeper | Detects 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.