Playbook
Mainnet Web3Modal and Wallet Hardening (Mainnet Web3modal Wallet Hardening)
From MetaMask-only to Web3Modal + WalletConnect + Coinbase WalletLink; harden chainId, gas, and addresses.
NFT Collection Mint & Mainnet Hardening
Part 4 of 4
CBD All-Stars and Confident Gorillas mint surfaces: WIP whitelist funnel, IPFS metadata, placeholder→reveal pipeline, and Web3Modal mainnet wallet hardening.
Mainnet breaks the single-wallet assumption
Gorillas mainnet 1.8 drops the All-Stars/1.4 window.ethereum.enable() path. Web3Modal opens with cacheProvider; WalletConnect (Infura id) and custom Coinbase WalletLink (chainId: 1) join providerOptions. ConnectWeb3 → ethers Web3Provider → address; contract instances speak through the modal provider.
Before (All-Stars / early)
window.ethereum only → Web3
After (mainnet 1.8)
Web3Modal
├─ WalletConnect (Infura)
├─ WalletLink / Coinbase (chainId 1)
└─ injected (MetaMask)
↓
provider → ethers Web3Provider
↓
getNftContract(Web3(provider))
This part closes the series with a mainnet readiness checklist: wallet diversity, network asserts, gas, and address/ABI discipline.
Concepts, defined where they first appear
📦 Web3Modal
Multi-wallet connect UI; WalletConnect/WalletLink injected via providerOptions.
📦 WalletConnect
QR / mobile wallet bridge; needs an Infura (or RPC) id.
📦 WalletLink
Coinbase Wallet connector; configured with appName, networkUrl, chainId.
📦 cacheProvider
Remembers the last successful provider; reconnect UX with stale-session risk.
RPC / Infura project ids are configuration — never secrets in the playbook. Keys live in env, not the repo.
Why MetaMask-only is not enough
Landing FAQ may still say MetaMask; mainnet users want Coinbase Wallet and mobile WalletConnect. A single injected path cuts conversion. 1.8 decision: multi-connector modal, network: 'mainnet'.
User segments
Desktop MetaMask
Mobile WC
Coinbase WalletLink
↓
one ConnectWeb3() API
Connection lifecycle
ConnectWeb3: web3Modal.connect() → Web3Provider → getAddress. DisconnectWeb3: clearCachedProvider + provider.disconnect + reload. getNetwork reads netId from the provider. Contract getters build Web3.eth.Contract from initialState.provider — the old appWeb3() Infura fallback must not be the mainnet signing path.
connect → address
getNetwork → chainId check
mint/reveal only if chainId OK
disconnect → clear cache → reload
ADR: Web3Modal as wallet facade
Decision: mint/reveal utils must not bind directly to window.ethereum; the modal facade supplies address and provider. Plus: adding a connector is a product decision. Minus: dual ethers + web3 stack and cacheProvider staleness. Mitigation: assert chainId before mint; clear cache on disconnect.
ADR-04 Web3Modal facade
UI → ConnectWeb3 / DisconnectWeb3
Utils → provider from modal state
Guard → chainId === 1 before send
Reject → wrong net / no provider
Mainnet failure playbook
- Stub contract addresses in production — tx looks fine, state is wrong. 2) Wrong network — sign on Rinkeby/Goerli, mainnet supply unchanged. 3) gasPrice: 0 — underpriced in the mempool. 4) ABI mismatch — multipleMint/reveal encode fails. 5) Pinata/Infura keys in the client bundle — secret leak (never commit).
Hardening checklist
✓ real mainnet addresses
✓ chainId === 1 gate
✓ dynamic gas / tip
✓ ABI ↔ deployment lock
✓ secrets in env only
The mappings that get confused most often
❌ window.ethereum is enough; modal is luxury
✓ Multi-connector is a mainnet conversion requirement
❌ cacheProvider means always stay connected
✓ Clear stale providers with disconnect + clearCachedProvider
❌ Put Infura/Pinata keys in constants
✓ Keys in env/secret store; minimal scope to the client
A checklist for auditing your own system
- After connect, do you assert chainId === 1?
- Is provider non-null before mint send?
- Did gasPrice: 0 survive into the production build?
- Are nft/reserve/auction addresses distinct and mainnet?
- Were WalletConnect / WalletLink tried on real devices?
What to take away from this part
- The 1.8 jump: MetaMask-only → Web3Modal multi-wallet facade.
- Mainnet hardening is address, network, gas, ABI, and secret discipline.
- Disconnect and cache clear are part of reconnect UX.
Connecting a wallet is not a feature; signing on the wrong network is a silent production incident.
FAQ
Frequently asked questions
What is Web3Modal?
Multi-wallet connect UI; WalletConnect/WalletLink injected via providerOptions.
What is WalletConnect?
QR / mobile wallet bridge; needs an Infura (or RPC) id.
Is it true that "window.ethereum is enough; modal is luxury"?
Multi-connector is a mainnet conversion requirement
What does this part lock in?
This part closes the series with a mainnet readiness checklist: wallet diversity, network asserts, gas, and address/ABI discipline. Gorillas mainnet 1.8 drops the All-Stars/1.4 window.ethereum.enable() path. Web3Modal opens with cacheProvider; WalletConnect (Infura id) and custom Coinbase WalletLink (chainId: 1) join providerOptions. ConnectWeb3 → ethers Web3Provider → address; contract instances speak through the modal provider.
Engineering Principles Learned
- Put mint utils behind one wallet facade.
- Assert chainId, address, and gas before every send.
- Keep secrets out of the bundle; prove connectors on real devices.
Continue reading
Continue reading
Next in series
Placeholder URI and Owner Reveal Pipeline
Placeholder metadata at mint, uridata map, reveal(tokenId, uriHash), and an owner-only reveal surface.
Same series
IPFS (InterPlanetary File System) Metadata, Mint, and Multiple Mint
Infura IPFS + Pinata pin, metadata JSON, mint(tokenId, uri) and multipleMint: 0.1 ETH × n, 750 supply.
Same series
Collection Landing and WIP Whitelist Funnel
CBD All-Stars mint landing: #GETINTHEWIP, 750 slots, discount funnel, and how a MetaMask-only mint surface becomes a product.