Development of Lazy-Minting NFT
Standard NFT collection minting: the creator deploys a contract and pre-mints all tokens, paying gas for each. With 10,000 tokens on Ethereum mainnet at 30 gwei—that's 30–50 ETH just for minting. For an independent artist releasing 100 works, that's $3,000–5,000 upfront, before the first sale.
Lazy minting flips the model: an NFT exists only as a signed voucher until the first purchase. The buyer pays the gas to mint at purchase time. The creator bears zero costs until receiving revenue.
How Lazy Minting Works Cryptographically
Voucher = Signed Data Structure
The creator signs off-chain data with their private key. The voucher contains all parameters of the future NFT:
struct NFTVoucher {
uint256 tokenId;
uint256 minPrice; // minimum price in wei
string uri; // IPFS URI of metadata
bytes signature; // creator's signature
}
The signature is created via EIP-712 (typed structured data signing), not raw eth_sign. EIP-712 is important: users see readable data in MetaMask when signing, not a hex string. This protects against phishing—a fake domain can't create a valid EIP-712 signature for someone else's contract.
On-chain Signature Verification
When redeem(voucher, recipient) is called, the contract recovers the signer's address via ecrecover:
function _verify(NFTVoucher calldata voucher) internal view returns (address) {
bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
keccak256("NFTVoucher(uint256 tokenId,uint256 minPrice,string uri)"),
voucher.tokenId,
voucher.minPrice,
keccak256(bytes(voucher.uri))
)));
return ECDSA.recover(digest, voucher.signature);
}
If the recovered address matches MINTER_ROLE, the signature is valid and minting is allowed. The contract uses ECDSA from OpenZeppelin 5.x for safe recover.
Critical Lazy Minting Vulnerabilities
Replay attack—without a unique nonce or tokenId, the same signature can be used multiple times. Protection: check _exists(tokenId) before minting + tokenId is part of the signed data. An already-minted tokenId will revert.
Cross-contract replay—the signature is valid for a specific contract on a specific network. EIP-712 domain includes chainId and verifyingContract address. A signature from Ethereum mainnet won't pass verification on Polygon—they have different chainId. Different contracts—different verifyingContract—different domain separator.
Front-running voucher—technically, anyone who sees the voucher in the mempool can try to mint it for their own address. Protection: recipient is included in the signed data. The voucher is valid only for the specific recipient address.
Storing and Distributing Vouchers
Vouchers are stored off-chain—in the platform database or IPFS. The marketplace API issues a voucher when initiating a purchase. For open sales (anyone can buy), vouchers are public. For whitelist sales—vouchers are issued only to verified addresses.
Lazy Minting for Creator Platforms (Rarible, OpenSea Model)
On platforms like OpenSea, lazy minting works differently: the creator signs a voucher through the platform's service. The platform stores vouchers. On first sale—the platform calls mintAndTransfer or lazyMint. EIP-2981 royalties are included in the voucher data.
For a custom marketplace: the same principle, but the contract and signing service are under your control.
Stack and Tools
Contract: Solidity 0.8.x, OpenZeppelin ERC721URIStorage, EIP-712 via EIP712 from OpenZeppelin, AccessControl for MINTER_ROLE.
Backend: voucher generation service—Node.js with viem for signing, storage in PostgreSQL or Redis.
Frontend: wagmi hooks for writeContract, transaction state display, IPFS integration via NFT.Storage for metadata uploads when creating.
Tests: Foundry—test correct verification, test replay attacks (should revert), test cross-contract (should revert), fork-test on mainnet to verify ECDSA compatibility.
Process and Timeline
Contract Development (1–2 days): ERC-721 with lazy mint logic, EIP-712 signing, verification, royalties.
Voucher Service (0.5 days): API for generating and storing vouchers, signing via creator's private key.
Frontend Component (1 day): buy/mint page with wallet connect, transaction state display.
Testing (0.5 days): Foundry unit-tests including replay attacks.
Total: 3–5 days. For a platform with multiple creators and a vouchers management dashboard—1–2 weeks. Cost is calculated individually.







