Playbook
BareNFT: Roles, Pausable, and Per-Token URI (Barenft Roles Pausable Per Token Uri)
BareNFT combines ERC721 Enumerable/Burnable/Pausable with AccessControl and role-gated mint(to, id, uri). Trade-offs of per-token URI and pause.
Bare Crypto Solidity Marketplace Protocol
Part 2 of 5
BareNFT, reserve escrow, English auction, and NFT-gated BareToken emissions built in Remix IDE with OpenZeppelin v4.1 GitHub imports — no Hardhat/Foundry — covering ADRs, weak RNG, and emergency-power trade-offs.
Beyond the preset: who mints, who freezes
BareNFT starts from the OpenZeppelin ERC721PresetMinterPauserAutoId skeleton but replaces AutoId with an explicit mint(to, tokenId, tokenURI). MINTER_ROLE and PAUSER_ROLE are split via AccessControlEnumerable; each token stores its own URI path joined with a baseURI — flexible metadata under a trust model where the minter alone shapes the collection.
Deployer
├─ DEFAULT_ADMIN_ROLE
├─ MINTER_ROLE ──> mint(to, id, uri)
└─ PAUSER_ROLE ──> pause / unpause
|
v
ERC721Enumerable + Burnable + Pausable
|
v
tokenURI = baseURI + per-token path
This part locks BareNFT role boundaries, the per-token URI decision, and pause side effects on the marketplace.
Concepts, defined where they first appear
📦 AccessControl
Role-based permissions; finer mint/pause split than Ownable.
📦 ERC721Pausable
Emergency brake on transfers that also locks listing and auction escrow exits.
📦 Per-token URI
A distinct metadata path per tokenId, joined with baseURI.
📦 Role-gated mint
Only MINTER_ROLE may call mint(to, id, uri).
Role split improves security only if DEFAULT_ADMIN_ROLE is not a single wallet holding every role as cosmetics.
ADR: explicit tokenId instead of AutoId
Decision: mint signature is mint(address to, uint256 tokenId, string tokenURI); the operator chooses IDs instead of a counter. Rationale: collection catalog numbers, reserve listings, and auction history index by tokenId. Cost: colliding IDs revert in _mint; off-chain mint coordination becomes mandatory. This is an 'operator catalog authority' ADR rather than on-chain auto-increment.
Roles, pause, and the transfer hook
pause() requires PAUSER_ROLE; _beforeTokenTransfer chains Enumerable and Pausable. While paused, safeTransferFrom from BareNFTReserve or BareNFTAuction also stops — a powerful incident tool and a way for the operator to freeze the entire market.
PAUSER_ROLE
pause()
|
v
_beforeTokenTransfer
|
+-- marketplace escrow transfer BLOCKED
+-- peer-to-peer transfer BLOCKED
Failure: half-built mint fee surface and URI drift
setMintingCurrency is callable without an obvious role gate; mintingPrice is never set — a half-finished fee surface. Performance: Enumerable plus _beforeTokenTransfer on every move raises gas on a busy secondary market. On metadata, baseURI + path breaks the whole collection when the IPFS gateway shifts; per-token flexibility still depends on baseURI ops discipline. A leaked MINTER_ROLE can mint fake IDs and poison reserve listing history.
The mappings that get confused most often
❌ AccessControl is just Ownable with extra steps
✓ AccessControl separates mint and pause; Ownable collapses every power into one owner
❌ Pause only stops peer transfers; marketplaces are fine
✓ Escrow safeTransferFrom also reverts under pause; the whole market locks
❌ Per-token URI means decentralized metadata
✓ URI still trusts the minter and baseURI operator; pinning and gateway policy are separate
A checklist for auditing your own system
- Do DEFAULT_ADMIN, MINTER, and PAUSER live in one wallet or separate ones?
- Which open listings/auctions stall when pause flips on?
- Does changing baseURI break older tokenURI joins?
- How does the UI behave if the same tokenId is minted twice?
- Who can call half-built surfaces such as setMintingCurrency?
What to take away from this part
- BareNFT puts catalog authority on-chain via role-gated mint(to, id, uri).
- Pausable is a global kill switch for escrow marketplaces — document the trade-off.
- Per-token URI flexibility inherits baseURI operational debt.
A role split is a lock plan; if one admin holds every key, it is only a label.
FAQ
Frequently asked questions
What is AccessControl?
Role-based permissions; finer mint/pause split than Ownable.
What is ERC721Pausable?
Emergency brake on transfers that also locks listing and auction escrow exits.
Is it true that "AccessControl is just Ownable with extra steps"?
AccessControl separates mint and pause; Ownable collapses every power into one owner
What does this part lock in?
This part locks BareNFT role boundaries, the per-token URI decision, and pause side effects on the marketplace. BareNFT starts from the OpenZeppelin ERC721PresetMinterPauserAutoId skeleton but replaces AutoId with an explicit mint(to, tokenId, tokenURI). MINTER_ROLE and PAUSER_ROLE are split via AccessControlEnumerable; each token stores its own URI path joined with a baseURI — flexible metadata under a trust model where the minter alone shapes the collection.
Engineering Principles Learned
- Choose the mint signature (auto-id vs explicit id) together with catalog and marketplace indexes.
- Design and communicate pause as a global stop for escrow protocols.
- A metadata ADR must cover baseURI, per-token path, and gateway pinning together.
Continue reading
Continue reading
Next in series
BareNFTReserve: Escrow, RandomBuy, and Weak RNG
Owner-only createNewListing, buy, and randomBuy using keccak256(revealNonce, block.difficulty, msg.sender) % 3 — weak RNG, owner-operated marketplace ADR…
Next in series
Remix + OpenZeppelin Delivery Without Hardhat
How the Bare Crypto protocol compiled and deployed in Remix IDE with OpenZeppelin v4.1 GitHub imports — no Hardhat/Foundry — and which ADR trade-offs…
Same series
BareNFTAuction: Claim, Refund, and Emergency Powers
English auction flows: bid, claim, cancel, below-reserve refunds, and owner emergency transferNft/transferFunds trade-offs.