What Are NFTs, Technically?

Non-Fungible Tokens (NFTs) are blockchain tokens where each token has a unique identifier that distinguishes it from every other token. Unlike ERC-20 tokens (where one token is interchangeable with any other), each NFT is distinct. This uniqueness makes NFTs suitable for representing ownership of digital art, collectibles, game items, music, domain names, and any other unique digital asset.

At the technical level, an NFT is a smart contract entry that maps a unique token ID to an owner address, with optional metadata that describes what the token represents. The smart contract enforces ownership rules, transfer logic, and approval mechanisms.

At StrikingWeb, we have been building blockchain applications since 2019, and the NFT explosion of 2021 has brought a wave of clients looking to launch NFT projects. This guide covers the technical fundamentals you need to understand before building.

Token Standards — ERC-721 and ERC-1155

ERC-721: The Original NFT Standard

ERC-721 is the foundational NFT standard on Ethereum. Each ERC-721 contract represents a collection of unique tokens, where each token has a distinct ID and a single owner. The standard defines the interface that all NFT contracts must implement:

// Core ERC-721 interface
interface IERC721 {
    function balanceOf(address owner) external view returns (uint256);
    function ownerOf(uint256 tokenId) external view returns (address);
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    function approve(address to, uint256 tokenId) external;
    function getApproved(uint256 tokenId) external view returns (address);
    function setApprovalForAll(address operator, bool approved) external;
    function isApprovedForAll(address owner, address operator) external view returns (bool);
}

Using OpenZeppelin's battle-tested implementation is the standard approach. You extend their ERC-721 contract and add your custom logic:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/utils/Counters.sol";

contract MyNFTCollection is ERC721URIStorage, Ownable {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIds;

    uint256 public mintPrice = 0.05 ether;
    uint256 public maxSupply = 10000;

    constructor() ERC721("MyCollection", "MNC") {}

    function mint(string memory tokenURI) public payable returns (uint256) {
        require(msg.value >= mintPrice, "Insufficient payment");
        require(_tokenIds.current() < maxSupply, "Max supply reached");

        _tokenIds.increment();
        uint256 newTokenId = _tokenIds.current();

        _safeMint(msg.sender, newTokenId);
        _setTokenURI(newTokenId, tokenURI);

        return newTokenId;
    }
}

ERC-1155: The Multi-Token Standard

ERC-1155 supports both fungible and non-fungible tokens within a single contract. This is useful for game items (where you might have unique legendary weapons but also stacks of identical potions), event tickets (unique VIP passes alongside general admission), and any project that needs both unique and identical tokens.

ERC-1155 is also more gas-efficient than ERC-721 for batch operations. Transferring 10 different NFTs costs roughly the same as transferring one, compared to 10 separate transactions with ERC-721.

Metadata and IPFS

NFT metadata — the name, description, image, and attributes of a token — is typically stored off-chain because storing large files on Ethereum is prohibitively expensive. The smart contract stores a URI that points to the metadata, and the metadata follows a standard JSON format:

{
  "name": "Cosmic Dragon #142",
  "description": "A rare cosmic dragon from the Ethereal Collection",
  "image": "ipfs://QmX9x7h4f3t2k1m9n8p7q6r5s4t3u2v1w0x/142.png",
  "attributes": [
    { "trait_type": "Element", "value": "Cosmic" },
    { "trait_type": "Rarity", "value": "Legendary" },
    { "trait_type": "Power Level", "display_type": "number", "value": 95 },
    { "trait_type": "Wings", "value": "Nebula" }
  ]
}

Why IPFS Over Centralized Storage

IPFS (InterPlanetary File System) is the preferred storage solution for NFT metadata and images because it is decentralized and content-addressed. A file's address is derived from its content (a hash), which means the content cannot be changed without changing the address. This provides immutability guarantees that centralized storage (like S3) cannot.

If you store metadata on a traditional server, the server owner can change the image or attributes after the NFT is sold — effectively changing what the buyer purchased. With IPFS, once content is pinned, its hash (CID) is permanent. The metadata URI stored in the smart contract points to a specific hash, and any change would produce a different hash.

Services like Pinata and NFT.Storage simplify IPFS hosting by providing pinning infrastructure and API access. For production NFT projects, we use Pinata for reliable IPFS pinning with redundancy across multiple nodes.

Marketplace Architecture

An NFT marketplace consists of several components working together:

Smart Contracts

Backend Services

Frontend Application

Web3 Frontend Integration

The frontend connects to the blockchain through Web3 libraries. We use ethers.js for its clean API and TypeScript support:

import { ethers } from 'ethers';

// Connect to the user's wallet
async function connectWallet() {
  if (window.ethereum) {
    const provider = new ethers.providers.Web3Provider(window.ethereum);
    await provider.send("eth_requestAccounts", []);
    const signer = provider.getSigner();
    const address = await signer.getAddress();
    return { provider, signer, address };
  }
  throw new Error("No Ethereum wallet detected");
}

// Mint an NFT
async function mintNFT(signer, contractAddress, tokenURI, price) {
  const contract = new ethers.Contract(contractAddress, NFT_ABI, signer);
  const tx = await contract.mint(tokenURI, {
    value: ethers.utils.parseEther(price)
  });
  const receipt = await tx.wait();
  return receipt;
}

Security Considerations

Smart contract security is paramount because deployed contracts are immutable and handle real money. Key security practices include:

Gas Optimization

Ethereum gas costs are a significant concern for NFT projects. Gas optimization strategies include:

The technical challenge of building an NFT marketplace is not in the token contract — that is relatively straightforward. The real complexity lies in the indexing infrastructure, the user experience around wallet interactions, and the security of the marketplace contract that handles listings and payments.

Building Your NFT Project

Whether you are launching an art collection, building a marketplace, or integrating NFTs into an existing platform, the technical foundations are the same: robust smart contracts, reliable metadata storage, efficient indexing, and a polished user experience. At StrikingWeb, we have built NFT platforms from concept to launch and can guide you through the technical decisions that will determine your project's success.

Share: