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.

5 min readUpdated 3 August 2026contracts, deployment, gas, withdrawal, pause

/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:

Marketplace contract addresses
Marketplace information for a chain
Contract balance
Marketplace-wide statistics

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

Deploys a marketplace contract

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

Updates the deployed contract's fee and fee recipient

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

Pauses marketplace trading on a chain
Resumes marketplace trading on a chain

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

Withdraws accumulated marketplace fees

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

Adds or removes a collection from the marketplace whitelist

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

  1. The master wallet has native currency on every chain you deployed to. Every collection deployment and every auction contract comes out of it.

  2. 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.

  3. The contract fee and the settings fee match. Compare the card on this screen against the Fees tab in Settings.

  4. nft_marketplace_address_<chain> exists if you want pause to work.

  5. 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.