Ethereum NFT Minting Pipelines: From Solidity to OpenSea

August 20, 2024

Over the past few years of freelancing on Fiverr, I've built more NFT minting systems than I can count. Here's a consolidated guide covering everything from writing your Solidity contract to listing your collection on OpenSea.

The Full Pipeline

Artwork/Metadata → IPFS Upload → Smart Contract Deployment → Minting → OpenSea Listing

Step 1: Writing the ERC-721 Contract

The quickest way to get a production-ready ERC-721 contract is to use OpenZeppelin's battle-tested implementations:

// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract MyNFT is ERC721URIStorage, Ownable { using Counters for Counters.Counter; Counters.Counter private _tokenIds; uint256 public constant MAX_SUPPLY = 10000; uint256 public mintPrice = 0.05 ether; constructor() ERC721("MyNFT", "MNFT") Ownable(msg.sender) {} function mint(string calldata tokenURI) external payable returns (uint256) { require(_tokenIds.current() < MAX_SUPPLY, "Max supply reached"); require(msg.value >= mintPrice, "Insufficient ETH"); _tokenIds.increment(); uint256 newId = _tokenIds.current(); _safeMint(msg.sender, newId); _setTokenURI(newId, tokenURI); return newId; } function withdraw() external onlyOwner { payable(owner()).transfer(address(this).balance); } }

Step 2: Uploading Metadata to IPFS

NFT metadata should be stored on IPFS, not a centralized server. I use nft.storage (now web3.storage) for free, pinned IPFS storage.

import requests import json def upload_to_ipfs(name: str, description: str, image_path: str) -> str: # First upload the image with open(image_path, "rb") as f: response = requests.post( "https://api.nft.storage/upload", headers={"Authorization": f"Bearer {NFT_STORAGE_KEY}"}, files={"file": f} ) image_cid = response.json()["value"]["cid"] # Then upload the metadata JSON metadata = { "name": name, "description": description, "image": f"ipfs://{image_cid}", "attributes": [] } response = requests.post( "https://api.nft.storage/upload", headers={ "Authorization": f"Bearer {NFT_STORAGE_KEY}", "Content-Type": "application/json" }, data=json.dumps(metadata) ) return response.json()["value"]["cid"]

Step 3: Minting with web3.py

from web3 import Web3 w3 = Web3(Web3.HTTPProvider("https://mainnet.infura.io/v3/YOUR_KEY")) contract = w3.eth.contract( address=CONTRACT_ADDRESS, abi=CONTRACT_ABI, ) def mint_nft(wallet_address: str, private_key: str, metadata_cid: str): token_uri = f"ipfs://{metadata_cid}" mint_price = w3.to_wei(0.05, "ether") tx = contract.functions.mint(token_uri).build_transaction({ "from": wallet_address, "value": mint_price, "gas": 200000, "nonce": w3.eth.get_transaction_count(wallet_address), }) signed = w3.eth.account.sign_transaction(tx, private_key) tx_hash = w3.eth.send_raw_transaction(signed.rawTransaction) return tx_hash.hex()

OpenSea Integration

Once your NFT is minted, it automatically appears on OpenSea (for Ethereum mainnet and most testnets). OpenSea reads the tokenURI from your contract and fetches the metadata from IPFS.

Key tips for OpenSea compatibility:

  • Always follow the OpenSea metadata standards
  • Include attributes in your metadata for trait filtering
  • Use a background_color field for better visual display

Testing on Sepolia

Never deploy to mainnet without testing on a testnet first:

# Get Sepolia ETH from faucet # Deploy with Hardhat npx hardhat run scripts/deploy.js --network sepolia

Building NFT pipelines taught me a lot about EVM internals, gas optimization, and decentralized storage. If you're building one, feel free to reach out!

GitHub
LinkedIn