Marketplace contracts
Deploying, configuring, pausing and withdrawing from the on-chain marketplace contract — the only NFT admin screen that spends money, and the traps in each of its four tabs.
/admin/nft/marketplace is the screen that makes the product real. Everything
else moderates rows; this one deploys contracts, changes on-chain configuration
and moves money out of a smart contract. Every button on it costs gas.
It has four tabs: Configuration, Emergency Controls, Revenue Withdrawal and Analytics. Above them sits a per-chain card showing the contract address, its balance in native currency, a fiat conversion and the deployment date.
The screen itself is gated by access.nft. Every panel on it calls endpoints
gated by access.nft.admin. An operator with access.nft alone sees the page
load and then watches every panel fail with a permission error.
Grant both, or neither.
Where the contract addresses come from
Two public endpoints back the header cards:
The first two require no authentication — they are the same data the public marketplace uses to decide which chains to show. The balance is admin-only.
Deploying
One contract per chain. The deploy panel takes five values:
| Field | Default | Validated |
|---|---|---|
chain |
ETH |
Must have an ACTIVE Ecosystem master wallet |
feeRecipient |
master wallet address | Must be a valid 0x… 40-hex address |
feePercentage |
2.5 |
0–10, rejected outside; stored in the contract as basis points |
listingFee |
0 |
Flat native-token fee the contract charges on listing |
maxRoyaltyPercentage |
10 |
0–50 in the schema; this is the contract's own ceiling |
The deployment signs with the master wallet's decrypted private key, at a 4,000,000 gas limit, against live gas prices. If the master wallet is empty the call fails with an RPC error rather than a clear message — check the balance first.
On success it writes a row to nft_marketplace with status: 'ACTIVE' and
network: 'mainnet', and marks any previously ACTIVE row for the same chain and
network as DEPRECATED.
A second deploy on the same chain returns 409 unless you pass force: true.
Forcing succeeds — and every existing on-chain listing, every approval creators
granted, and the accumulated fee balance all still belong to the old contract.
The platform will happily point new sales at the new address while old listings
quietly stop working.
Withdraw the old contract's balance before you force a redeploy, and expect to ask creators to re-approve.
The chain list a user sees is derived from this table, so:
- Deploying a new chain makes it appear in the collection-create form.
- Deprecating the last ACTIVE row for a chain removes it, and existing collections on that chain can no longer be created against — though the ones already there keep working.
Configuration
Requires chain and contractAddress; feePercentage (0–10) and
feeRecipient are both optional so you can change one without the other.
This sends a transaction. It costs gas and it takes a block to confirm.
The settings screen writes nftMarketplaceFeePercentage, which governs the
custodial offer path only. This endpoint changes what the contract
charges on fixed-price sales and auctions.
If you only change one, the same item sells for two different net amounts depending on how the buyer bought it. See Fees and royalties.
Emergency controls
Both require contractAddress and a reason. The pause state is recorded in
platform settings as nft_marketplace_paused_<chain> with the reason, the
admin's id and a timestamp in nft_marketplace_pause_reason_<chain>. Pausing
twice returns 409.
Both routes first look up the setting nft_marketplace_address_<chain> and
return 404 if it is absent or does not match the address you passed. The
deploy route records the address in the nft_marketplace table, not in that
setting.
On an install where that key was never written by hand, pause and unpause answer
"Marketplace contract not found" no matter what you send. If you need the
emergency controls, write the key first — nft_marketplace_address_eth,
nft_marketplace_address_bsc and so on, lower-cased chain, value equal to the
contract address.
A second caveat: the shipped NFTMarketplace contract does not implement
OpenZeppelin's Pausable. The service checks whether the ABI exposes a paused
function and, when it does not, reports "not paused" rather than failing. Treat
the pause flag as a platform-side signal that your own screens honour, not
as a guarantee that the chain will refuse a transaction.
Revenue withdrawal
Fees from on-chain sales accumulate as a native-token balance inside the marketplace contract. They are not revenue until this runs.
| Field | Required | Behaviour when omitted |
|---|---|---|
chain |
Yes | — |
contractAddress |
Yes | — |
reason |
Yes | Rejected. Recorded, up to 500 characters |
amount |
No | Withdraws the entire available balance |
withdrawalAddress |
No | Sends to the contract's configured fee recipient |
The response reports the transaction hash, the amount withdrawn, the gas used and the remaining balance.
There is no alert, no scheduled sweep and no dashboard tile for a growing contract balance. It is visible only on this screen, only while you are looking at it. Set yourself a recurring reminder.
Collection whitelisting
There is no interface for this. It is an API-only control that tells the
marketplace contract whether a given NFT contract address is supported. It takes
chain, marketplaceAddress, nftContract, a supported boolean and a
reason.
Use it when you need to stop the marketplace contract from handling a specific collection at the chain level, rather than merely suspending its row.
What to check before you launch
-
The master wallet has native currency on every chain you deployed to. Every collection deployment and every auction contract comes out of it.
-
The fee recipient is an address you control. It defaults to the master wallet, which is fine — but if you passed a custom address, confirm you hold its key. Withdrawals default there.
-
The contract fee and the settings fee match. Compare the card on this screen against the Fees tab in Settings.
-
nft_marketplace_address_<chain>exists if you want pause to work. -
You know the deployed contract address and have it recorded somewhere outside the platform. A forced redeploy makes the old one hard to find, and the balance stays in it.
Related
- Install — the first deployment, in context.
- Fees and royalties — the two places revenue accumulates.
- Troubleshooting — when a deployment or a sale fails.