On-chain
EVM — Seaport attribution
Exclusivo EVM sales settle on canonical OpenSea Seaport, not on a contract of ours. Attribution is therefore a convention on the order struct — this page is the whole of it, including the parts that do not work.
The attribution markers
Every Seaport order Exclusivo builds — listings and collection offers alike — carries two markers. Both are set in buildExclusivoOrderAttribution() and neither touches order economics.
1. zone — the crawlable marker
The order's zone is set to the Exclusivo platform fee recipient:
0xCD93552140bF932842Dbc767D0c6Cc75F2BA680COrders stay FULL_OPEN; the zone is never given execution authority, so it cannot block or alter a fill. It exists purely because Seaport echoes it in the OrderFulfilled log, which gives you a one-field test for "is this ours". The same value is published as marketplaceContracts.evmFeeRecipient in the manifest; treat the manifest as authoritative if the two ever differ.
2. salt — the order-struct brand
The order salt is built as a constant 8-byte brand prefix followed by 24 random bytes:
0x455843313a534561 // + 24 random bytessalt is not present in OrderFulfilled, so it is useless for log scanning. It brands the order struct itself, which means you can use it when you hold the order — order JSON from an API, fulfillment calldata, or the order-hash preimage.
The OrderFulfilled log
Sales are emitted by canonical Seaport at these addresses on every chain we trade on:
| Version | Address |
|---|---|
| Seaport 1.5 | 0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC |
| Seaport 1.6 | 0x0000000000000068f116a894984e2db1123eb395 |
event OrderFulfilled(
bytes32 orderHash,
address indexed offerer,
address indexed zone,
address recipient,
SpentItem[] offer,
ReceivedItem[] consideration
);
// topic0
0x9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31Because zone is indexed, you can filter server-side: topic0 = the hash above, topic2 = the zone address, left-padded to 32 bytes. That is a cheap, exact query on any log provider.
{
"address": ["0x0000000000000068f116a894984e2db1123eb395", "0x00000000000000ADc04C56Bf30aC9d3c0aAF14dC"],
"topics": [
"0x9d9af8e38d66c62e2c12f0225249fd9d721c54b83f48d9352c97c6cacdcb6f31",
null,
"0x000000000000000000000000cd93552140bf932842dbc767d0c6cc75f2ba680c"
]
}Decode recipe
For a listing fill (the common case: someone buys a listed NFT), decode the matched log like this.
for each log where topic0 == OrderFulfilled and emitter in {Seaport 1.5, 1.6}:
if zone != 0xCD93552140bF932842Dbc767D0c6Cc75F2BA680C: skip // not an Exclusivo order
offer item (ERC721) -> collection, tokenId
offerer -> seller
recipient -> buyer
consideration items:
item paid to the offerer -> price (seller proceeds / ask)
item paid to the zone addr -> marketplaceFee (200 bps of price)
any remaining item -> royaltyAmount, royaltyRecipient
itemType NATIVE -> currency is the chain-native asset
itemType ERC20 -> currency is that token (WETH on accepted offers)
orderHash -> orderId
tx hash -> tx
block.timestamp -> timestampThe consideration array is ordered as built, but do not rely on index positions — identify each item by its recipient. The seller leg is the item paid to offerer, the fee leg is the item paid to the zone address, and whatever is left is royalty.
Fees are on top, not deducted
price is the seller ask and is exactly what the seller receives. The buyer paid price + marketplaceFee + royaltyAmount. If you sum every consideration item and call the result the sale price, your floor and volume figures will run high against every other source.Accepted collection offers
When a seller accepts a collection offer, the same event fires but the roles invert, because the offerer is now the buyer. Handling this wrong is the fastest way to publish a sale with the parties swapped.
| Field | Listing fill | Accepted collection offer |
|---|---|---|
| Offer item | The ERC-721 | WETH (the currency) |
| Consideration | The payment legs | Includes the ERC-721 |
offerer | Seller | Buyer |
recipient | Buyer | Seller |
| Seller identity | offerer | The token's previous owner — read Transfer.from for that ERC-721 in the same receipt |
| Currency | Usually native | ERC-20 (WETH) |
A reliable discriminator: if the offer array contains an ERC-721 item it is a listing fill; if the consideration array contains the ERC-721 it is an accepted offer.
Caveats you must handle
Orders created before 2026-07-26 have zone 0x0
zone = address(0) and is indistinguishable from any other Seaport order. The only heuristic available is a consideration item paying exactly 200 bps of the seller leg to our fee recipient, which is weak and will produce both false positives and false negatives. For historical EVM sales, use the public API.ApeChain is not attributable at all
OrderFulfilled from us. ApeChain activity is served by the API only — if your pipeline is chain-complete only when every chain resolves on-chain, ApeChain will silently be missing.The external-aggregator fee is not an Exclusivo sale
- OpenSea prepared fulfillment (required on Ink) still emits the same
OrderFulfilledcarrying our zone — attribution holds, no special case needed. - Both Seaport versions are live. Filter on 1.5 and 1.6 together; an indexer watching only one will lose sales without erroring.
- A cancelled listing may still be fillable. We do not call
cancelOrdersfor listings, so an order cancelled in our book remains valid on-chain until expiry. A fill of such an order still carries our zone and is still a real sale.
Launchpad (primary market)
The launchpad ERC-721 is Exclusivo's own bytecode, deployed fresh per collection, so primary-market activity is fully on-chain. Contracts from the v3.1 template revision (2026-07-26 onward) emit:
// One event per mint, full economics.
// Buyer total = paid + platformFee.
event Minted(uint256 indexed phaseId, address indexed to, uint256 quantity,
uint256 paid, // phase.price * quantity (creator proceeds)
uint256 platformFee); // per-mint platform fee * quantity (treasury)
// Full phase payload — no archival view calls needed.
event PhaseAdded(uint256 indexed phaseId, Phase phase);
event PhaseUpdated(uint256 indexed phaseId, Phase phase);
// Phase = (uint8 kind, bytes32 merkleRoot, address gateToken, uint256 minBalance,
// uint256 price, uint64 startTime, uint64 endTime,
// uint32 maxPerWallet, uint32 maxSupply)
// kind: 0 = public, 1 = allowlist, 2 = ERC-721 gated, 3 = ERC-20 gated
// Reserved (airdrop) deliveries, distinguishable from paid mints.
event ReservedMinted(address indexed to, uint256 quantity);
// Royalty config — ERC-2981 itself is silent.
event RoyaltySet(address receiver, uint96 bps);
// ERC-4906 — refresh metadata on reveal / baseURI change.
event BatchMetadataUpdate(uint256 _fromTokenId, uint256 _toTokenId);Legacy signatures still in service
Already-deployed contracts are immutable, so older signatures remain live. An indexer must decode both forms or it will silently skip mints on older collections.
| Deploy era | Minted | PhaseAdded / PhaseUpdated |
|---|---|---|
| v2 (legacy) | None — only the ERC-721 Transfer | None — phase config is not observable |
| v3, before 2026-07-26 | Minted(uint256 indexed phaseId, address indexed to, uint256 quantity, uint256 paid) — paid excludes the per-mint platform fee, so true buyer spend is not recoverable from logs | (uint256 indexed phaseId, uint8 kind) — config requires a phases(id) view call |
| v3.1 (current) | The 5-field form above | Full Phase payload |
v2 mint prices are unrecoverable