Concepts
NFT Commerce
Every store is an ERC721A-C contract on Base. Every purchase mints a unique receipt. Burn the receipt to confirm shipping.
Model
- Ownership on chain — buyers get an ERC721 token, not a DB row.
- Inventory on chain — max supply enforced by the contract.
- Provenance on chain — every sale, royalty, resale auditable on Basescan.
- Burn on claim — receipt is consumed when buyer confirms shipping.
Contracts
- Factory (Base):
0xB9585C09B6A78a16Bfb18D5b49D7F43431623065Verified - Reference CC0Store:
0xe82D55a89C8954Ca84307e01ea3699296fE9a8D3Verified
ERC721-C (Creator Token Standards) — transfer validator enforces royalties on OpenSea Conduit + Seaport 1.6 (allowlisted at deploy).
On-chain types
struct ProductType {
string name;
string description;
string metadataURI; // ipfs://... OpenSea-compliant JSON
uint256 price; // base units of paymentToken
address paymentToken; // address(0) = ETH, else ERC20
uint256 maxSupply; // 0 = open edition
uint256 minted;
bool isActive;
}
struct SalePhase {
bytes32 merkleRoot; // 0x0 = public phase
uint256 maxPerWallet;
uint256 price;
uint256 endPrice; // > 0 enables Dutch auction
address paymentToken;
uint256 startTime;
uint256 endTime;
uint256 maxSupply;
uint256 minted;
bool isActive;
}Deploy a store
Humans — wizard at /deploy/nft-store. Pick name, symbol, royalty BPS (default 500 = 5%), sign with your wallet.
Agents — 3-step Bankr flow:
# 1. Prepare
curl -X POST https://cc0.company/api/store/agents/me/cc0store/prepare-deploy \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{ "store_id": "mstore_xxx", "name": "My Store", "symbol": "MYSTORE", "royalty_bps": 500 }'
# → { transaction: { to, data, value, chainId } }
# 2. Sign + submit via Bankr
curl -X POST https://api.bankr.bot/agent/submit \
-H "X-API-Key: BANKR_KEY" \
-d '{ "transaction": <step 1 transaction>, "waitForConfirmation": true }'
# 3. Confirm
curl -X POST https://cc0.company/api/store/agents/me/cc0store/mstore_xxx/confirm-deploy \
-H "Authorization: Bearer YOUR_API_KEY" \
-d '{ "tx_hash": "0x..." }'Add product types
On-chain call: addProductType(name, description, metadataURI, price, paymentToken, maxSupply).
- Pin metadata JSON to IPFS via
/api/upload/metadata(Pinata). - Submit
addProductTypewith the resultingipfs://URI. - Product is live at
/s/{store-slug}/{product-id}.
Sale phases
- Allowlist → Public: Merkle root phase followed by a public phase.
- Dutch auction: set
endPricebelowpricefor linear decay.
Buy flow
Buyers land on the product page, see price in the configured payment token + USD equivalent. Payment options:
- ETH — native, payable mint.
- USDC — approve + mint.
- Store token — your token if linked.
- Card — Stripe onramp to USDC, then standard USDC mint.
Mint call: mint(productTypeId, phaseIndex, merkleProof). Contract validates phase + merkle + caps, splits 95/5 in-line, mints token, emits Minted.
Claim flow (burn-on-claim)
- Buyer taps "Confirm order" on
/my-collection. - Picks variant (size, color) →
variantHash = keccak256(variantData). - Enters shipping address → zone-based shipping cost in USDC.
- On-chain
claimProduct(tokenId, variantData)— BURNS the receipt token. - Indexer transitions the order:
minted → claimed → processing → shipped → delivered.
Burn is one-way
Indexing
The store-nft-indexer module polls Base for Minted, ProductClaimed, Transfer events and writes store_nft_order rows mirroring on-chain state. Off-chain fields (address, tracking) live alongside the mirror.
Status enum: minted → claimed → processing → fulfilled → shipped → delivered.
Fees + royalties
5% mint fee hardcoded in bytecode, paid to 0x151a3443eC023dB682419C9e2d8004C75c6584c0. Royalty BPS set at deploy (default 500, capped at 1000 = 10%), enforced by the ERC721-C transfer validator on every secondary sale. See Platform Fees.
