Case StudiesJan 20267 min read

Smart Contracts in Production: What I Learned Building an NFT Marketplace

Shipping ERC721/ERC1155 contracts for an audio NFT marketplace rewires how you think about deployment: no hotfixes, no rollbacks, every bug is permanent. The habits that survive contact with immutability.

DILJOT SINGH · TECHNICAL LEAD

Before building an NFT audio marketplace, my mental model of deployment risk came from web backends: ship carefully, monitor, roll back if needed. Ethereum deleted the last step. A deployed contract is immutable — every bug is permanent, every funds-handling mistake is potentially unrecoverable, and "we'll patch it Monday" is not a sentence that exists. Building and deploying ERC721 and ERC1155 contracts under those rules changed habits I've kept ever since.

Standards first, creativity never

The marketplace needed unique audio pieces (ERC721) and editioned releases (ERC1155). The single best decision was treating OpenZeppelin's audited implementations as the foundation and keeping our custom logic — minting rules, royalty handling, marketplace escrow — as thin extensions on top. In most software, novel code is an asset. In contracts, every novel line is attack surface. The expensive exploits in this space overwhelmingly live in bespoke logic that reimplemented something a standard already did safely.

Testing like the rollback doesn't exist

The mindset shift: on a web backend, tests raise confidence. Here, tests are the deployment safety net in its entirety. We treated a deploy like a hardware tape-out — a date you prepare for, not a button you press.

Gas is a product constraint, not an optimization

Every storage write costs users real money at unpredictable prices. That reshaped the architecture: metadata and audio lived off-chain (IPFS) with the contract storing only hashes; batch operations used ERC1155's native batching rather than loops of single transfers; and anything computable off-chain stayed off-chain. The discipline resembles embedded programming more than web development — you count bytes and writes, because your users pay for them per operation.

The dApp boundary is where users get hurt

Most user-facing failures weren't contract bugs — they were the Web3.js/MetaMask integration layer: dropped transactions the UI treated as failures (they later succeeded — double-mint risk), chain reorgs briefly rewriting recent history, users on the wrong network signing no-ops. The frontend has to model transaction state honestly: pending is a real state that can last minutes, confirmed-once is not confirmed, and every action the UI offers must be safe to submit twice. Idempotency again — the same discipline as Celery tasks, with higher stakes.

What carried over to my non-blockchain work