On-chain
Solana — memo format
There is no Exclusivo Solana program. Marketplace actions are transfers involving a custodial escrow wallet, and from 2026-07-26 each one carries a structured SPL memo that makes it machine-decodable.
The crawl anchor
Every Exclusivo Solana marketplace transaction carries exactly one SPL Memo instruction (program MemoSq4gqABAXKb96qnH8TysNcWxMyWCqXgDLGmfcHr) whose UTF-8 payload is:
exclusivo:v1:<compact JSON>The anchor is two conditions, and you want both:
- The memo payload starts with
exclusivo:v1:, and - the platform escrow wallet is a signer or a counterparty in the same transaction.
Escrow wallet
HUxx919u8CSpyzLcmDYvBrvzBNt7caCdgDv2Ucjm4fgeThe memo prefix alone is not a security boundary: anyone can write the same bytes into their own transaction. Requiring the escrow wallet as signer or counterparty is what makes the match meaningful.
Payload keys
Keys are single letters because Solana transactions are capped at 1232 bytes and the memo competes with real instructions for that budget. Any key whose value is unknown is omitted entirely rather than sent as null — treat every key except t as optional.
| Key | Type | Meaning |
|---|---|---|
t | string | Event type — see the table below. Always present. |
lid | string | Listing id — the normalized orderId and the cross-transaction join key. |
m | base58 | Asset mint, Core asset address, or cNFT asset id. Case-sensitive. |
c | base58 | Collection mint, when known at build time. |
p | string | Price in lamports, decimal string. The seller ask. |
cur | string | Currency. Always "SOL" today. |
s | base58 | Seller wallet. |
b | base58 | Buyer wallet. |
f | string | Marketplace fee in lamports (300 bps of p). |
r | string | Total royalty in lamports. |
rt | base58 | Royalty recipient — the first/primary creator only. |
x | number | Listing expiry, unix seconds. |
std | string | legacy | core | cnft. |
n | number | Item count. Bulk purchases only. |
lids | string[] | Listing ids in a bulk purchase. Truncated, or dropped entirely, to fit the transaction — see Limitations. |
Event types
| t | Transaction | Signer | Normalized type |
|---|---|---|---|
| list | Seller deposits the NFT into escrow | Seller | listed |
| cancel | Escrow returns the NFT to the seller (delist, expiry cron, recovery, reclaim) | Escrow | cancelled |
| sale | Legacy atomic sale — NFT release and every payment leg in one transaction | Buyer + escrow | sold |
| sale_pay | Core/cNFT payment transaction (SOL legs only) | Buyer | sold (half) |
| sale_release | Core/cNFT NFT release transaction | Escrow | sold (half) |
| sale_bulk | Bundled purchase, up to 10 items in one transaction | Buyer + escrow | sold, once per item |
| offer_deposit | Buyer deposits SOL into offer escrow | Buyer | not a v1 event |
| offer_settle | Instant-sell settlement payouts from escrow | Escrow | sold |
| offer_refund | Offer deposit refunded | Escrow | not a v1 event |
Payload contents vary by type: sale, sale_pay, and offer_settle carry the full economics (lid,m,c,p,cur,s,b,f,r,rt,std); sale_release carries lid,m,b,std; cancel carries lid,m,s,std; list carries m,c,p,cur,s,x,f,r,rt,std but no lid.
Worked examples
A listing
exclusivo:v1:{"t":"list","m":"7Nx4Qk...","c":"85Ghq5...","p":"12500000000","cur":"SOL","s":"9WzQ2f...","f":"375000000","r":"625000000","rt":"3JmPfd...","x":1785110400,"std":"core"}Reads as: seller 9WzQ2f… deposited Core asset 7Nx4Qk… into escrow, asking 12.5 SOL. A buyer will pay 12.5 SOL to the seller plus 0.375 SOL platform fee and 0.625 SOL royalty, for 13.5 SOL total. The listing expires at unix 1785110400. There is no lid on a list memo.
A Core sale — payment leg
exclusivo:v1:{"t":"sale_pay","lid":"kQ7bTm2xR9","m":"7Nx4Qk...","c":"85Ghq5...","p":"12500000000","cur":"SOL","s":"9WzQ2f...","b":"4Ht8Lv...","f":"375000000","r":"625000000","rt":"3JmPfd...","std":"core"}A Core sale — release leg
exclusivo:v1:{"t":"sale_release","lid":"kQ7bTm2xR9","m":"7Nx4Qk...","b":"4Ht8Lv...","std":"core"}A cancel
exclusivo:v1:{"t":"cancel","lid":"kQ7bTm2xR9","m":"7Nx4Qk...","s":"9WzQ2f...","std":"legacy"}A cancel memo covers every return path: a seller delisting, the expiry cron, the recovery cron, and an escrow reclaim. The memo does not say which. If you need that distinction, take it from the API.
Decoding
for each transaction where the escrow wallet is a signer or counterparty:
memo = the one SPL Memo instruction's UTF-8 data
if not memo.startsWith("exclusivo:v1:"): skip
event = JSON.parse(memo.slice("exclusivo:v1:".length))
switch (event.t):
"list" -> listed
"cancel" -> cancelled
"sale" | "sale_bulk" -> sold, complete in this transaction
"offer_settle" -> sold
"sale_pay" | "sale_release" -> half a sale; join on event.lid
"offer_deposit" | "offer_refund" -> not a v1 event, ignore
// amounts are lamports as decimal strings — parse as BigInt, never NumberJoining a two-tx sale
Core and compressed (cNFT) sales settle as two transactions: a buyer-signed payment transaction carrying the SOL legs (sale_pay) and a later escrow-signed release transaction carrying the NFT movement (sale_release). Both memos carry the same lid and the same m.
sold(core | cnft) := sale_pay(lid) join sale_release(lid)
price, fees, parties <- sale_pay
provenance, finality <- sale_releaseA sale is final when the release lands, not when the payment lands. Do not publish a sale on the payment transaction alone; if the release never lands you will have published a sale that did not happen.
Release transactions can look duplicated
skipPreflight and retried with fresh blockhashes, so more than one signature can appear for the same release even though only one lands. Deduplicate on lid, never on signature.Naive indexers get this wrong
lid, a release transaction reads as an unpaid NFT transfer out of the platform wallet, and the payment transaction reads as SOL moving for no reason. That was the situation before 2026-07-26 and it is why history from earlier dates has to come from the API.Reading the transfer legs
The buyer always pays fees on top of the ask, so the buyer's total is p + f + r. Inside a sale transaction the system-transfer legs are:
| Leg | Amount | Notes |
|---|---|---|
| Buyer → seller | p | The ask. Exactly what the seller receives. |
| Buyer → escrow | f | The platform fee, plus any royalty remainder that could not be paid out directly. |
| Buyer → creator(s) | r | The royalty total, split pro-rata across creators from DAS metadata. |
Prefer the memo over summing the legs
f and r record the intended split and are the correct source.Limitations
List memos carry no lid
list memo cannot contain it. You cannot join a listing transaction to its listing record from chain data alone. Join through the API instead (the listing record carries escrowTxSig), or approximately by (mint, seller) plus recency — which is a heuristic and will mis-attribute if the same wallet relists the same asset quickly.The memo degrades, and sometimes disappears
t, m, s|b, std, and if it still does not fit, release and return transactions drop the memo entirely rather than fail settlement over logging. On bulk purchases, lids is truncated — and dropped whole rather than partially garbled — while n is always preserved. A missing or minimal memo is expected behaviour, not corruption: your decoder must tolerate an event with only t and a mint, and must not treat a memo-less escrow transfer as a non-Exclusivo transaction.Recovery-path listings have no transaction at all
escrowTxSig: 'recovery' — the asset is in escrow but the listing was reconstructed rather than created by a deposit. There is no transaction and therefore no memo. These listings are API-only by definition, and a chain-only index will not see them.One address does several jobs
f per event; the address separation is a product decision we have not made.- History before 2026-07-26 has no structured memos — earlier transactions carried static English prose identical across standards. Use the API for backfill.
rtis the primary creator only. When royalty is split across several creators, the memo names the first; the full split is visible in the transfer legs, subject to the rent-folding caveat above.- The SolGods blind auction program is out of scope. It emits Anchor events about bids, not listings or sales. Auction NFT releases still go through the escrow path and carry the memos documented here.