Minting 100 NFTs on Polygon for Under $10 in Gas
Minting 100 NFTs on Polygon for Under $10 in Gas

Quantum Genesis NFT #2 — arte generativa quântica

Quantum Genesis NFT #2 — arte generativa quântica

Quantum Genesis NFT #100 — peça final da coleção
Deploying and minting 100 NFTs on Ethereum mainnet would have cost us $500 or more. On Polygon, we did it for under $10. Here is exactly how, step by step, with all the code.
1. Why Polygon?
When we set out to mint the Quantum Genesis collection — 100 NFTs generated from real quantum computers — we needed a chain that met three requirements:
- Low gas fees: We wanted minting costs to be negligible so the art and provenance could be the focus, not the transaction fees.
- Ethereum compatibility: Full EVM support means the same Solidity contract, the same tools (web3.py, ethers.js), and the same developer experience as Ethereum mainnet.
- OpenSea support: Polygon is a first-class citizen on OpenSea. Collections appear instantly, and buyers can purchase with MATIC or bridged ETH without extra friction.
Polygon (chain ID 137) checked every box. Gas fees are measured in fractions of a cent per transaction, and finality is fast — typically under 5 seconds.
2. Our Approach: Self-Contained Solidity Contract
Most NFT tutorials start with "import OpenZeppelin." We went a different direction. Our ERC-721 contract is entirely self-contained — zero external dependencies.
Why?
- Smaller bytecode: No unused code from a library. Our contract compiles to exactly what we need.
- No dependency risk: No supply-chain concerns. Every line of code is ours to audit.
- Educational value: Building ERC-721 from scratch teaches you what the standard actually requires.
The contract implements:
- ERC-721 — the NFT standard (balanceOf, ownerOf, transferFrom, approve, etc.)
- ERC-721 Enumerable — on-chain token enumeration (totalSupply, tokenByIndex, tokenOfOwnerByIndex)
- EIP-2981 — royalty info (5% royalties on secondary sales)
- Batch minting — mint multiple tokens in a single transaction
- Custom errors — gas-efficient error handling instead of require strings
3. Step 1: Write the Contract
Here are the key parts of our Solidity contract. The symbol is QGEN with a max supply of 100.
Constructor
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
contract QuantumGenesisNFT {
string public name = "Quantum Genesis";
string public symbol = "QGEN";
uint256 public constant MAX_SUPPLY = 100;
uint96 public constant ROYALTY_BPS = 500; // 5%
address public owner;
string private _baseTokenURI;
uint256 private _tokenIdCounter;
// Custom errors (cheaper than require strings)
error NotOwner();
error MaxSupplyReached();
error InvalidBatchSize();
error ZeroAddress();
constructor() {
owner = msg.sender;
}
}
Mint Function
function mint(address to) external returns (uint256) {
if (msg.sender != owner) revert NotOwner();
if (_tokenIdCounter >= MAX_SUPPLY) revert MaxSupplyReached();
if (to == address(0)) revert ZeroAddress();
uint256 tokenId = ++_tokenIdCounter;
_safeMint(to, tokenId);
return tokenId;
}
Batch Mint
function batchMint(address to, uint256 count) external returns (uint256[] memory) {
if (msg.sender != owner) revert NotOwner();
if (count == 0 || count > 50) revert InvalidBatchSize();
if (_tokenIdCounter + count > MAX_SUPPLY) revert MaxSupplyReached();
uint256[] memory tokenIds = new uint256[](count);
for (uint256 i = 0; i < count; i++) {
uint256 tokenId = ++_tokenIdCounter;
_safeMint(to, tokenId);
tokenIds[i] = tokenId;
}
return tokenIds;
}
EIP-2981 Royalties
function royaltyInfo(uint256, uint256 salePrice)
external view returns (address receiver, uint256 royaltyAmount)
{
return (owner, (salePrice * ROYALTY_BPS) / 10000);
}
Key takeaway: Custom errors likerevert NotOwner()cost significantly less gas thanrequire(msg.sender == owner, "Not the owner"). The string in a require statement is stored in the contract bytecode and costs gas to deploy and execute.
4. Step 2: Compile with solcx (Python)
We used py-solc-x to compile the contract from Python. This keeps the entire pipeline — compile, deploy, mint — in a single language.
# compile_contract.py
import solcx
from pathlib import Path
# Install the Solidity compiler version
solcx.install_solc("0.8.20")
# Read the contract source
source = Path("nft-contracts/QuantumGenesisNFT.sol").read_text()
# Compile
compiled = solcx.compile_source(
source,
output_values=["abi", "bin"],
solc_version="0.8.20"
)
# Extract ABI and bytecode
contract_id, contract_interface = compiled.popitem()
abi = contract_interface["abi"]
bytecode = contract_interface["bin"]
# Save for deployment
import json
Path("nft-output/abi.json").write_text(json.dumps(abi, indent=2))
Path("nft-output/bytecode.txt").write_text(bytecode)
print(f"Compiled. Bytecode size: {len(bytecode) // 2} bytes")
The compilation is fast — under 2 seconds. The output is an ABI (the contract's interface definition) and the bytecode (what gets deployed to the blockchain).
5. Step 3: Deploy with web3.py
Deployment uses web3.py to connect to Polygon's RPC, build the deployment transaction, sign it, and broadcast it.
# deploy_and_mint.py (deployment section)
from web3 import Web3
import json, os
# Connect to Polygon
w3 = Web3(Web3.HTTPProvider("https://polygon-rpc.com"))
assert w3.is_connected(), "Failed to connect to Polygon"
# Load compiled contract
abi = json.loads(open("nft-output/abi.json").read())
bytecode = open("nft-output/bytecode.txt").read()
# Account setup
private_key = os.environ["PRIVATE_KEY"]
account = w3.eth.account.from_key(private_key)
# Build deployment transaction
contract = w3.eth.contract(abi=abi, bytecode=bytecode)
tx = contract.constructor().build_transaction({
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
"gasPrice": w3.eth.gas_price,
"chainId": 137
})
# Sign and send
signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
contract_address = receipt.contractAddress
print(f"Deployed at: {contract_address}")
Our contract deployed to: 0x488fCfaEA5fDf1cF6BAED5e8A34D7858033E1a27
After deployment, we set the baseURI to point to our IPFS metadata folder:
# Set base URI to IPFS metadata CID
base_uri = "ipfs://bafybeif7z767txkebwz7iqlgkvikvhxciuxt2kxenmjad4bbzx4rfioyc4/metadata/"
tx = contract.functions.setBaseTokenURI(base_uri).build_transaction({
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
"gasPrice": w3.eth.gas_price,
"chainId": 137
})
signed = account.sign_transaction(tx)
w3.eth.send_raw_transaction(signed.raw_transaction)
6. Step 4: Batch Minting Strategy
We minted in two phases:
Phase 1: Individual Mints (#1 through #18)
The first 18 NFTs were minted one at a time. This was our initial testing phase — verifying that metadata resolved correctly on OpenSea, that images rendered, and that royalty info was picked up.
Phase 2: Batch Mints (#19 through #100)
Once everything was validated, we switched to batch minting for the remaining 82 tokens. Five transactions covered all of them:
| Batch | Tokens | TX Hash (prefix) |
|---|---|---|
| 1 | #19 – #38 (20) | 1c8222... |
| 2 | #39 – #58 (20) | 94cced... |
| 3 | #59 – #72 (14) | 0b52b2... |
| 4 | #73 – #86 (14) | 2c0726... |
| 5 | #87 – #100 (14) | e402ec... |
# Batch minting example
def batch_mint(contract, w3, account, to_address, count):
tx = contract.functions.batchMint(to_address, count).build_transaction({
"from": account.address,
"nonce": w3.eth.get_transaction_count(account.address),
"gasPrice": w3.eth.gas_price,
"chainId": 137
})
signed = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed.raw_transaction)
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print(f"Minted {count} tokens. Gas used: {receipt.gasUsed}")
return receipt
# Mint remaining 82 in batches
wallet = "0xa198032b224f6087cE83B8b499921AD64A5D2e00"
for batch_size in [20, 20, 14, 14, 14]:
batch_mint(contract, w3, account, wallet, batch_size)
Why not mint all 82 at once? Polygon has a block gas limit. Minting 50+ tokens in a single transaction can exceed it. Batches of 14–20 kept us safely within limits while still being far more efficient than individual mints.
7. Step 5: Verify on PolygonScan
After deployment, we verified the contract source on PolygonScan. This lets anyone read the code and confirm that the contract does what it claims. Verified contracts also get a green checkmark on PolygonScan and enable direct interaction through the "Read Contract" and "Write Contract" tabs.
You can view our verified contract at:
polygonscan.com/address/0x488f...1a27
8. Gas Cost Analysis
Here is the real cost breakdown for minting 100 NFTs on Polygon:
| Operation | Transactions | Approx. Cost (MATIC) | Approx. Cost (USD) |
|---|---|---|---|
| Contract deployment | 1 | ~0.05 | ~$0.03 |
| Set baseURI | 1 | ~0.001 | <$0.01 |
| Individual mints (#1-18) | 18 | ~0.09 | ~$0.05 |
| Batch mints (#19-100) | 5 | ~0.15 | ~$0.09 |
| Total | 25 | ~0.3 MATIC | Well under $1 |
Yes, you read that correctly. The total gas cost for deploying a smart contract and minting 100 NFTs was well under $1 on Polygon. Even accounting for network congestion spikes, you would struggle to spend more than $10.
9. Polygon vs Ethereum: The Gas Reality
To put this in perspective:
| Operation | Polygon | Ethereum Mainnet |
|---|---|---|
| Deploy contract | ~$0.03 | $50 – $200 |
| Single mint | <$0.01 | $3 – $15 |
| 100 mints total | <$1 | $300 – $1500 |
For an art project where the value proposition is quantum-generated provenance — not speculative floor prices — spending hundreds of dollars on gas makes no sense. Polygon let us focus our budget on what actually matters: IPFS pinning, quantum computer access, and the art itself.
The bottom line: Polygon is not a "lesser" chain. It is a pragmatic choice for any NFT project where gas costs should not be the main expense. Every MATIC we saved on gas was money we could invest in real quantum computing time.
What's Next
With all 100 tokens minted on-chain, the next piece of the puzzle was metadata and IPFS storage. How do you structure NFT metadata so that quantum provenance is verifiable and permanent? That is the subject of Post 8: IPFS, Metadata, and Immutable Quantum Provenance.
Desenvolvo sistemas que usam hardware quântico real (IBM Quantum, Origin Quantum) para gerar arte e NFTs com proveniência verificável on-chain. Escrevo sobre computação quântica aplicada, criptografia, Web3 e arte generativa — tudo com código que roda em processadores quânticos de verdade.
Comentários
Postar um comentário