Mainnet Smoke Test Guide
How to validate the full Diamond NFT system on Arbitrum One before launching real collections. The process: deploy test infrastructure, exercise every feature, abandon the test contracts, then redeploy clean for production.
Prerequisites
| Requirement | Details |
|---|---|
| Environment built | pnpm env:build staging — generates .env.staging in each package |
| Deployer wallet funded | The wallet behind NFT_DEPLOYER_PRIVATE_KEY needs ETH on Arbitrum One for gas |
| Sepolia tests passing | Full lifecycle should already work on Arbitrum Sepolia |
| Contract tests passing | pnpm nft:test — all tests green |
| emprops-api running | Staging or local instance, pointed at mainnet chain config |
Warning
This process uses real ETH for gas on Arbitrum One. Costs are low (typical Arbitrum L2 gas — see Cost Estimate), but make sure the deployer wallet is funded.
Phase 1: Deploy Infrastructure
pnpm nft:deploy:mainnetThis deploys the full Diamond infrastructure to Arbitrum One:
- All shared facets (ERC721, Mint, ContractURI, MetadataUpdate)
- CollectionInit initializer
- DiamondFactory
It writes all contract addresses to packages/nft-contracts/deployments/arbitrum-one.json. Open that file and confirm all addresses are present.
Optionally verify contracts on Arbiscan so the source code is publicly readable — this also lets you interact with contracts through Arbiscan's Read/Write UI.
Phase 2: Deploy a Test Collection
Use the Monitor NFT Admin UI (Collections tab) to deploy a throwaway collection. Enter a test collection's UUID and deploy it to chain 42161 (Arbitrum One).
The monitor handles the full flow: calls the DiamondFactory on-chain, writes the contract address back to the DB, and sets mint_status = "deployed".
What to confirm:
- Transaction succeeded on Arbiscan
- Collection record in DB has
contract_addressandchain_id = 42161 - On-chain state is correct:
name(),symbol(),maxSupply(),mintActive() = true,minter()= deployer address
You can check on-chain state through the Monitor UI or directly on Arbiscan.
Phase 3: Test Minting
Mint via API (metadata pipeline)
The standard mint path goes through emprops-api, which builds metadata and mints on-chain:
POST /nft/collections/:testCollectionId/mint
{
"recipient": "<your-wallet-address>",
"chainId": 42161
}This builds ERC-721 metadata from the collection's generation config, calls MintFacet.mintTo() on-chain, creates a deck record, and stores the token metadata.
What to confirm: totalSupply() incremented, token appears in recipient wallet on Arbiscan, tokenURI() returns the correct metadata URL.
Mint via Monitor (direct on-chain)
Use Monitor → Mint tab to mint directly without the metadata pipeline. This calls the contract's mintTo() directly.
What to confirm: Second token minted, totalSupply() incremented again.
Verify Metadata
The metadata endpoints are public (no auth):
GET /nft/metadata/:testCollectionId/collection.json
GET /nft/metadata/:testCollectionId/0Confirm contract-level metadata (ERC-7572) and token metadata (name, image, attributes) serve correctly and the image URL resolves.
Phase 4: Test Admin Operations
All admin operations go through Monitor → Admin tab, or directly via the monitor's admin API. Each call sends a transaction through the deployer wallet.
Toggle Minting
Disable minting and confirm mintActive() = false and mint attempts fail. Re-enable and confirm minting works again.
Update Base URI
Change the base URI and confirm tokenURI() resolves to the new path.
Update Max Supply
Lower the max supply and confirm minting beyond the limit fails.
Transfer Minter Role
Change the minter address and confirm the old minter can no longer mint.
Phase 5: Verify External Visibility
Check that minted NFTs appear correctly on third-party platforms:
- Collection appears on Arbiscan with correct name/symbol
- Token transfers visible in transaction history
- NFT appears on OpenSea (Arbitrum) — may take a few minutes to index
- Metadata renders correctly on OpenSea (name, image, attributes)
contractURI()serves valid ERC-7572 collection metadata
Tip
OpenSea indexes Arbitrum One automatically. If metadata doesn't appear, use OpenSea's "Refresh metadata" button on the token page, or wait 10-15 minutes for their indexer.
Phase 6: Abandon Test Contracts
Once all checks pass, decommission the test infrastructure. This is irreversible — by design.
Decommission the Test Collection
POST /nft/admin/decommission
{
"chainId": 42161,
"collectionAddress": "<test-collection-address>"
}This endpoint handles the full teardown:
- Calls
setMintActive(false)for immediate protection - Reads all facets via DiamondLoupe's
facets() - Executes
diamondCutwithaction=REMOVEfor every non-infrastructure facet (ERC721, Mint, ContractURI, etc.) - Resets DB state:
mint_status→not_deployed, nullscontract_addressand related fields
The contract still exists on-chain as a dead Diamond shell — only infrastructure facets remain (loupe, ownership, ERC165). All business functions revert.
Decommission the Factory
POST /nft/factory/decommission
{
"chainId": 42161
}This endpoint handles the full factory teardown:
- Bricks ALL deployed collections on chain 42161 (same decommission as above, for each one)
- Transfers factory ownership to
0x000000000000000000000000000000000000dEaD(irreversible burn) - Deletes the
nft_factory_deploymentDB record
After this, the factory contract exists on-chain but can never create collections again — owner() is the burn address.
Danger
Factory decommission is irreversible. The factory can never create collections again. This is by design — you'll deploy a fresh factory for production.
Phase 7: Production Deploy
With the smoke test complete, deploy fresh production infrastructure:
pnpm nft:deploy:mainnetThis deploys a brand new factory and facets. The old test contracts are abandoned on-chain but have no connection to the new deployment.
What to confirm:
- New
arbitrum-one.jsonwritten with fresh contract addresses (different from the test deploy) - Factory
owner()= deployer address (not burn address) - Ready to create real collections
Post-Deploy
- Commit
packages/nft-contracts/deployments/arbitrum-one.jsonto the repo - Verify the Monitor UI shows the correct factory for chain 42161
- Consider transferring factory ownership to a multi-sig for production security
Quick Reference
| # | Step | Command / Action |
|---|---|---|
| 1 | Deploy test factory + facets | pnpm nft:deploy:mainnet |
| 2 | Deploy test collection | Monitor UI → Collections tab |
| 3 | Mint via API (metadata pipeline) | POST /nft/collections/:id/mint |
| 4 | Mint via Monitor (direct) | Monitor UI → Mint tab |
| 5 | Verify metadata endpoints | GET /nft/metadata/:id/collection.json |
| 6 | Test admin operations | Monitor UI → Admin tab |
| 7 | Verify on Arbiscan + OpenSea | Manual check |
| 8 | Decommission test collection | POST /nft/admin/decommission |
| 9 | Decommission test factory | POST /nft/factory/decommission |
| 10 | Fresh production deploy | pnpm nft:deploy:mainnet |
| 11 | Commit deployment JSON | git add packages/nft-contracts/deployments/arbitrum-one.json |
Cost Estimate
Arbitrum One gas is cheap. Rough estimates for the full smoke test:
| Operation | Estimated Cost |
|---|---|
| Deploy factory + facets + init | ~$1-3 |
| Deploy test collection | ~$0.50-1 |
| Mint 2-3 tokens | ~$0.10-0.30 |
| Admin operations (4-5 txns) | ~$0.20-0.50 |
| Decommission collection + factory | ~$0.20-0.60 |
| Total smoke test + teardown + production redeploy | ~$4-10 |
Tip
Check Arbiscan Gas Tracker before starting. Gas varies but the full cycle should stay well under $10.
