🎨 NFT Foundations

🎯 What are NFTs?

Non-Fungible Tokens (NFTs) are unique digital assets on a blockchain that represent ownership of a specific item — digital art, collectibles, virtual real estate, or other unique items.

Key Concepts

Fungibility: Unlike cryptocurrencies (fungible), NFTs are unique and cannot be exchanged one-for-one. Blockchain: NFTs are secured on Ethereum, Solana, and other chains. Ownership: Proof of ownership is stored on-chain.

📌 Unique📌 Blockchain📌 Ownership
// Example: Simple NFT ownership structure struct NFT { uint256 tokenId; address owner; string metadataURI; bool isTransferable; } // Minting a new NFT function mintNFT(address to, string memory uri) public returns (uint256) { uint256 tokenId = _tokenIds.current(); _tokenIds.increment(); _mint(to, tokenId); _setTokenURI(tokenId, uri); return tokenId; }

📜 ERC-721 & ERC-1155

🎯 Token Standards

ERC-721 is the standard for non-fungible tokens. ERC-1155 supports both fungible and non-fungible tokens in one contract.

Standard Comparison

ERC-721: Each token is unique, separate transfer operations per token. ERC-1155: Batch transfers, semi-fungible tokens, efficient for gaming.

📜 ERC-721📜 ERC-1155🔄 Batch Transfers
import openzepplin // ERC-721 Implementation contract MyNFT is ERC721 { constructor() ERC721("MyNFT", "MNFT") {} function safeMint(address to, uint256 tokenId) public { _safeMint(to, tokenId); } } // ERC-1155 Implementation contract MyCollection is ERC1155 { constructor() ERC1155("https://api.example.com/token/{id}.json") {} function mint(address to, uint256 id, uint256 amount) public { _mint(to, id, amount, ""); } }

📋 Metadata Standards

🎯 On-Chain & Off-Chain Metadata

Metadata describes the NFT's properties, attributes, and content. It can be stored on-chain or off-chain (IPFS, centralized servers).

Metadata Structure

Standard fields: name, description, image, attributes. Dynamic attributes: Trait types, levels, stats. Storage: IPFS recommended for decentralization.

📋 JSON Schema🌐 IPFS📊 Attributes
{ "name": "CryptoKitty #1234", "description": "A cute digital cat with unique traits.", "image": "ipfs://QmX...", "attributes": [ {"trait_type": "Fur", "value": "Orange"}, {"trait_type": "Eyes", "value": "Blue"}, {"trait_type": "Rarity", "value": "Legendary"} ], "external_url": "https://cryptokitties.co/1234" }

🔧 Smart Contract Development

🎯 Writing NFT Contracts

Smart contracts are the backbone of NFTs — they handle minting, transferring, burning, and implementing business logic.

Contract Architecture

Inheritance: Use OpenZeppelin contracts. Access control: Ownable, roles, modifiers. Events: Transfer, Approval events for indexing.

🔧 Solidity📦 OpenZeppelin🔒 Security
// Complete NFT contract import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; contract MyNFT is ERC721, Ownable { uint256 private _tokenIdCounter; mapping(uint256 => string) private _tokenURIs; constructor() ERC721("MyNFT", "MNFT") {} function mint(address to, string memory uri) public onlyOwner { uint256 tokenId = _tokenIdCounter; _tokenIdCounter++; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } }

🏪 Marketplace Mechanics

🎯 Buying, Selling, Trading

NFT marketplaces facilitate peer-to-peer trading of NFTs with listing, bidding, and escrow mechanisms.

Marketplace Features

Listings: Fixed price or auction. Bidding: Highest bid wins. Royalties: Creator fees on secondary sales.

🏪 Listings🔨 Auctions💰 Royalties
// Marketplace listing struct struct Listing { address seller; uint256 tokenId; uint256 price; bool isAuction; uint256 endTime; address highestBidder; uint256 highestBid; } function createListing(uint256 tokenId, uint256 price) public { require(ownerOf(tokenId) == msg.sender); listings[tokenId] = Listing(msg.sender, tokenId, price, false, 0, address(0), 0); } function buy(uint256 tokenId) public payable { Listing memory listing = listings[tokenId]; require(msg.value >= listing.price); // Transfer ownership and funds }

💰 Royalties & Fees

🎯 Creator Compensation

Royalties allow creators to earn a percentage of secondary sales, incentivizing long-term value creation.

Royalty Implementation

EIP-2981: Standard for royalty payments. Percentage: Usually 5-10% of sale price. On-chain vs off-chain: Some marketplaces handle royalties separately.

💰 EIP-2981📊 Percentage🔄 Secondary Sales
import "@openzeppelin/contracts/interfaces/IERC2981.sol"; contract RoyaltyNFT is ERC721, IERC2981 { address private _royaltyRecipient; uint256 private _royaltyPercentage; function royaltyInfo(uint256 tokenId, uint256 salePrice) external view override returns (address receiver, uint256 royaltyAmount) { return (_royaltyRecipient, (salePrice * _royaltyPercentage) / 100); } function setRoyaltyRecipient(address recipient) public onlyOwner { _royaltyRecipient = recipient; } }

🌐 IPFS & Decentralized Storage

🎯 Storing NFT Data

IPFS (InterPlanetary File System) provides decentralized, content-addressed storage for NFT metadata and assets.

IPFS Benefits

Content-addressed: Files identified by their hash (CID). Permanent: Files persist as long as they're pinned. Decentralized: No single point of failure.

🌐 IPFS📦 CID🔒 Permanent
import { create } from 'ipfs-http-client' // Connect to IPFS const ipfs = create({ url: 'https://ipfs.infura.io:5001' }) // Upload NFT metadata async function uploadMetadata(metadata) { const result = await ipfs.add(JSON.stringify(metadata)) console.log('IPFS CID:', result.path) return `ipfs://${result.path}` } // Pin file for persistence async function pinFile(file) { const result = await ipfs.add(file) await ipfs.pin.add(result.cid) return result.cid.toString() }

🖨️ Minting Processes

🎯 Creating New NFTs

Minting is the process of creating a new NFT by writing to the blockchain, assigning ownership, and setting metadata.

Minting Types

Single mint: Create one token. Batch mint: Create multiple tokens at once. Presale mint: Limited access minting.

🖨️ Gas Fees📦 Batch🔒 Presale
// Minting with gas optimization function mint(uint256 amount) public payable { require(amount > 0 && amount <= MAX_MINT); require(totalSupply() + amount <= MAX_SUPPLY); require(msg.value >= amount * MINT_PRICE); for (uint256 i = 0; i < amount; i++) { uint256 tokenId = totalSupply() + 1; _safeMint(msg.sender, tokenId); } } // Mint to multiple addresses function mintBatch(address[] calldata to, uint256[] calldata amounts) public onlyOwner { require(to.length == amounts.length); for (uint256 i = 0; i < to.length; i++) { // Mint amounts[i] tokens to to[i] } }

💤 Lazy Minting

🎯 Mint on Demand

Lazy minting defers on-chain minting until the first sale, saving gas costs for creators and enabling free mints.

Lazy Mint Benefits

Gas savings: No upfront minting cost. Customer pays gas: Buyer pays gas when claiming. Scalable: Mint unlimited tokens without cost.

💤 No Upfront Cost🔄 On-Demand📈 Scalable
// Lazy minting with signatures contract LazyMintNFT is ERC721 { mapping(bytes32 => bool) public usedSignatures; function mintWithSignature( address to, string memory uri, uint256 tokenId, bytes memory signature ) public { bytes32 message = keccak256(abi.encodePacked(to, uri, tokenId)); require(!usedSignatures[message]); address signer = recoverSigner(message, signature); require(signer == owner()); usedSignatures[message] = true; _safeMint(to, tokenId); _setTokenURI(tokenId, uri); } }

🧩 Fractional NFTs

🎯 Fractional Ownership

Fractional NFTs split a single NFT into multiple fungible tokens, enabling shared ownership and liquidity.

Fractionalization Process

Vault: Lock NFT in a smart contract. Tokens: Issue fungible tokens representing shares. Governance: Token holders vote on decisions.

🧩 Shares🏛️ Governance💰 Liquidity
contract FractionalNFT { address public nftContract; uint256 public nftId; ERC20 public shareToken; uint256 public totalShares; constructor(address _nft, uint256 _id, uint256 _shares) { nftContract = _nft; nftId = _id; totalShares = _shares; // Transfer NFT to this contract // Create share token } function buyShares(uint256 amount) public payable { // Buy shares with ETH } function vote(uint256 proposalId) public { // Governance voting } }

🔄 Dynamic NFTs

🎯 Changing NFT State

Dynamic NFTs can change their metadata, appearance, or properties based on external conditions, time, or user actions.

Dynamic Mechanisms

Time-based: Unlock new forms over time. User interaction: Change based on user actions. External data: Use oracles for real-world data.

🔄 Live Updates📊 Oracle Data🎮 Interactive
contract DynamicNFT is ERC721 { mapping(uint256 => string) public currentState; mapping(uint256 => uint256) public lastUpdate; function updateState(uint256 tokenId, string memory newState) public { require(ownerOf(tokenId) == msg.sender); require(block.timestamp - lastUpdate[tokenId] >= 1 days); currentState[tokenId] = newState; lastUpdate[tokenId] = block.timestamp; // Update metadata URI } function tokenURI(uint256 tokenId) public view override returns (string memory) { // Return dynamic URI based on current state return string(abi.encodePacked("ipfs://", currentState[tokenId])); } }

🔒 Security & Risks

🎯 Protecting NFTs

NFT security involves protecting against scams, phishing, contract vulnerabilities, and private key theft.

Security Best Practices

Smart contract audits: Professional auditing. Multi-sig wallets: Multiple approvals for transfers. Phishing awareness: Never share private keys.

🔒 Audits🔑 Multi-Sig🛡️ Phishing
contract SecureNFT is ERC721 { mapping(address => bool) public whitelist; mapping(uint256 => bool) public frozen; modifier onlyWhitelisted() { require(whitelist[msg.sender]); _; } function freeze(uint256 tokenId) public onlyOwner { frozen[tokenId] = true; } function transferFrom(address from, address to, uint256 tokenId) public override { require(!frozen[tokenId], "Token is frozen"); super.transferFrom(from, to, tokenId); } }

🔗 Interoperability

🎯 Cross-Chain NFTs

Interoperability enables NFTs to move between different blockchains, expanding utility and reach.

Bridge Solutions

Bridges: Lock and mint on another chain. Wormhole: Cross-chain messaging. LayerZero: Omnichain NFTs.

🔗 Bridges🌐 Cross-Chain🔄 Omnichain
// Cross-chain NFT with LayerZero contract OmnichainNFT is ERC721, OApp { using BytesLib for bytes; function crossChainTransfer(uint256 tokenId, uint32 dstChainId) public payable { bytes memory payload = abi.encode(tokenId, msg.sender); _lzSend(dstChainId, payload, payable(msg.sender), address(0), bytes("")); _burn(tokenId); } }

🚀 Future of NFTs

🎯 Emerging Trends

AI-generated NFTs, token-gated communities, decentralized identity, metaverse integration, and real-world assets.

Future Applications

Digital identity: Soulbound tokens. Real estate: Property tokenization. Education: Certificate NFTs. Gaming: Play-to-earn, metaverse.

🚀 Metaverse📜 Soulbound🏗️ RWA
// Soulbound tokens (non-transferable) contract SoulboundToken is ERC721 { function transferFrom(address from, address to, uint256 tokenId) public override { revert("Soulbound token cannot be transferred"); } function safeTransferFrom(address from, address to, uint256 tokenId) public override { revert("Soulbound token cannot be transferred"); } }