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.

Bare Crypto Solidity marketplace protocol diagram

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

  1. Do DEFAULT_ADMIN, MINTER, and PAUSER live in one wallet or separate ones?
  2. Which open listings/auctions stall when pause flips on?
  3. Does changing baseURI break older tokenURI joins?
  4. How does the UI behave if the same tokenId is minted twice?
  5. Who can call half-built surfaces such as setMintingCurrency?

What to take away from this part

  1. BareNFT puts catalog authority on-chain via role-gated mint(to, id, uri).
  2. Pausable is a global kill switch for escrow marketplaces — document the trade-off.
  3. 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

Next in series

Same series

Paylaş