> ## Documentation Index
> Fetch the complete documentation index at: https://docs.monad.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Build with NFTs

> How to build an NFT project on Monad: standards, a deploy quickstart, and the tooling for minting, marketplaces, indexing, wallets, and more.

export const MintCostCalculator = () => {
  const GAS = {
    "721a": 40000,
    standard: 85000
  };
  const DEPLOY_GAS = 2500000;
  const RPC_URL = "https://rpc.monad.xyz";
  const PRICE_URL = "https://coins.llama.fi/prices/current/coingecko:monad";
  const [count, setCount] = useState("10000");
  const [mintType, setMintType] = useState("721a");
  const [customGas, setCustomGas] = useState("85000");
  const [baseFeeGwei, setBaseFeeGwei] = useState("100");
  const [monPrice, setMonPrice] = useState(null);
  const [usdLoading, setUsdLoading] = useState(false);
  const [usdError, setUsdError] = useState(null);
  useEffect(() => {
    let cancelled = false;
    (async () => {
      try {
        const res = await fetch(RPC_URL, {
          method: "POST",
          headers: {
            "Content-Type": "application/json"
          },
          body: JSON.stringify({
            jsonrpc: "2.0",
            id: 1,
            method: "eth_getBlockByNumber",
            params: ["latest", false]
          })
        });
        const data = await res.json();
        const hex = data && data.result && data.result.baseFeePerGas;
        if (hex && !cancelled) setBaseFeeGwei(String(Math.round(parseInt(hex, 16) / 1e9)));
      } catch (e) {}
    })();
    return () => {
      cancelled = true;
    };
  }, []);
  const n = Math.max(0, Number(count) || 0);
  const baseFee = Math.max(0, Number(baseFeeGwei) || 0);
  const gasPerMint = mintType === "custom" ? Math.max(0, Number(customGas) || 0) : GAS[mintType];
  const toMon = gas => gas * baseFee * 1e-9;
  const totalGas = n * gasPerMint;
  const totalMon = toMon(totalGas);
  const perMintMon = toMon(gasPerMint);
  const deployMon = toMon(DEPLOY_GAS);
  const fmtMon = x => {
    if (!isFinite(x) || x === 0) return "0";
    if (x < 0.0001) return x.toExponential(2);
    if (x < 1) return x.toFixed(4).replace(/0+$/, "").replace(/\.$/, "");
    if (x < 1000) return x.toLocaleString(undefined, {
      maximumFractionDigits: 3
    });
    return Math.round(x).toLocaleString();
  };
  const fmtUsd = x => x < 0.01 ? "$" + x.toFixed(4) : "$" + x.toLocaleString(undefined, {
    maximumFractionDigits: 2
  });
  const loadUsd = async () => {
    setUsdLoading(true);
    setUsdError(null);
    try {
      const res = await fetch(PRICE_URL);
      const data = await res.json();
      const p = data && data.coins && data.coins["coingecko:monad"] && data.coins["coingecko:monad"].price;
      if (!p) throw new Error("no price");
      setMonPrice(p);
    } catch (e) {
      setUsdError("Could not load the MON price. The MON figures are always accurate.");
    } finally {
      setUsdLoading(false);
    }
  };
  const labelClass = "text-xs font-semibold text-gray-500 uppercase tracking-wide";
  const inputClass = "w-full rounded-lg border border-gray-950/10 dark:border-white/10 bg-background-light dark:bg-background-dark text-sm px-3 py-2";
  return <div className="not-prose rounded-lg border border-gray-950/10 dark:border-white/10 p-5 my-6">
      <div className="grid grid-cols-1 sm:grid-cols-2 gap-4">
        <div className="flex flex-col gap-1.5">
          <label className={labelClass}>Number of NFTs</label>
          <input className={inputClass} type="number" min="0" value={count} onChange={e => setCount(e.target.value)} />
        </div>
        <div className="flex flex-col gap-1.5">
          <label className={labelClass}>Mint type</label>
          <select className={inputClass} value={mintType} onChange={e => setMintType(e.target.value)}>
            <option value="721a">ERC-721A batch (~40k gas)</option>
            <option value="standard">Standard ERC-721 (~85k gas)</option>
            <option value="custom">Custom</option>
          </select>
        </div>
        {mintType === "custom" && <div className="flex flex-col gap-1.5">
            <label className={labelClass}>Gas per mint</label>
            <input className={inputClass} type="number" min="0" value={customGas} onChange={e => setCustomGas(e.target.value)} />
          </div>}
        <div className="flex flex-col gap-1.5">
          <label className={labelClass}>Base fee (gwei)</label>
          <input className={inputClass} type="number" min="0" value={baseFeeGwei} onChange={e => setBaseFeeGwei(e.target.value)} />
        </div>
      </div>

      <div className="mt-5 rounded-lg border border-gray-950/10 dark:border-white/10 p-4 bg-gray-500/[0.04] dark:bg-white/[0.02]">
        <div className="text-3xl font-semibold">
          {fmtMon(totalMon)} MON
          {monPrice != null && <span className="text-lg font-medium text-gray-500 dark:text-gray-400">
              {" "}
              ≈ {fmtUsd(totalMon * monPrice)}
            </span>}
        </div>
        <div className="text-sm text-gray-500 dark:text-gray-400 mt-1">
          to mint out {n.toLocaleString()} NFTs ({totalGas.toLocaleString()} gas total)
        </div>
        <div className="text-sm text-gray-500 dark:text-gray-400 mt-1">
          Per mint: {fmtMon(perMintMon)} MON
          {monPrice != null ? " (≈ " + fmtUsd(perMintMon * monPrice) + ")" : ""}
          {"  |  One-time deploy: "}
          {fmtMon(deployMon)} MON
        </div>

        {monPrice == null ? <button type="button" onClick={loadUsd} disabled={usdLoading} className="mt-3 inline-flex items-center rounded-lg border border-gray-950/10 dark:border-white/10 px-3 py-1.5 text-sm hover:bg-gray-50 dark:hover:bg-white/5 transition-colors disabled:opacity-50">
            {usdLoading ? "Loading..." : "Show USD estimate"}
          </button> : <div className="text-xs text-gray-500 dark:text-gray-400 mt-2">
            MON priced at {fmtUsd(monPrice)} (live, DeFiLlama)
          </div>}
        {usdError && <div className="text-sm text-red-500 mt-2">{usdError}</div>}
      </div>
    </div>;
};

Monad is fully EVM-compatible, so the NFT stack you already know works unchanged. Its high throughput and low fees also make mint-heavy and fully onchain NFTs practical.

## What you can build

Anything you can build on an EVM chain, plus a few things that are only comfortable when blockspace is cheap and fast:

* **Collections and PFPs**: standard ERC-721/1155 drops, allowlists, and reveals.
* **Fully onchain and dynamic NFTs**: store art or state onchain and mutate metadata on interaction.
* **High-frequency mints**: large collections and open editions that would be prohibitively expensive elsewhere.
* **Game and app assets**: items, passes, and rewards that live onchain.
* **Token-bound accounts**: give each NFT its own wallet with [ERC-6551](/guides/erc-6551).

## NFT standards

Monad executes standard EVM bytecode, so the usual standards and libraries work with no changes:

* **ERC-721**: the base non-fungible token standard.
* **ERC-1155**: multi-token standard for editions and semi-fungible items.
* **ERC-721A**: gas-optimized ERC-721 for cheap batch mints.
* **EIP-2981**: onchain royalty signaling. As on every chain, royalties are read by marketplaces, and enforcement is marketplace-dependent.
* **[ERC-6551](/guides/erc-6551)**: token-bound accounts that let each NFT own assets, interact with contracts, and maintain its own onchain identity.

Some popular implementations: [OpenZeppelin](https://docs.openzeppelin.com/contracts), [thirdweb](https://portal.thirdweb.com/contracts), [solady](https://github.com/Vectorized/solady), or [ERC721A](https://github.com/chiru-labs/ERC721A).

## Quickstart: deploy a collection

Deploy a minimal ERC-721 to Monad Testnet with [Foundry](/tooling-and-infra/toolkits/foundry).

**Prerequisites:** Foundry installed, and a funded testnet account. Monad Testnet is chain ID `10143` and mainnet is `143`. Get testnet funds and RPC endpoints from the [Testnet](/developer-essentials/testnet) page.

Set up the project and add OpenZeppelin:

```bash theme={null}
forge init my-collection && cd my-collection
forge install OpenZeppelin/openzeppelin-contracts
```

Write the contract:

```solidity title="src/MyCollection.sol" theme={null}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";

contract MyCollection is ERC721, Ownable {
    uint256 public nextId;

    constructor() ERC721("My Collection", "MYC") Ownable(msg.sender) {}

    function mint(address to) external onlyOwner {
        _safeMint(to, nextId++);
    }

    function _baseURI() internal pure override returns (string memory) {
        return "ipfs://YOUR_CID/";
    }
}
```

Deploy it:

```bash theme={null}
forge create src/MyCollection.sol:MyCollection \
  --rpc-url https://testnet-rpc.monad.xyz \
  --private-key $PRIVATE_KEY \
  --broadcast
```

<Note>
  Fund the deploying account before you deploy. Get testnet MON from the faucet linked on the [Testnet](/developer-essentials/testnet) page.
</Note>

Then mint the first token:

```bash theme={null}
cast send <COLLECTION_ADDRESS> "mint(address)" <YOUR_ADDRESS> \
  --rpc-url https://testnet-rpc.monad.xyz \
  --private-key $PRIVATE_KEY
```

That is a working collection. From here you can swap in an allowlist, a public mint price, or a batch-mint standard like ERC-721A, and wire in the tooling below.

## Choose your tooling

Everything an NFT project needs is live on Monad. Pick per layer.

### Minting and launchpads

No-code tools to create, deploy, and manage a collection:

* **[Scatter](https://www.scatter.art/)**: artist-first launchpad that deploys fully-owned ERC-721A/1155 collections through low-fee contract factories.
* **[thirdweb](https://thirdweb.com)**: prebuilt Drop contracts and a claim UI.

### Marketplaces

List and trade on marketplaces live on Monad:

* **[OpenSea](https://opensea.io)**: including SeaDrop for primary mints.
* **[Scatter](https://www.scatter.art/)**: buy and sell collections, compatible with other NFT marketplaces.

### Indexing and NFT APIs

Read balances, ownership, metadata, and transfer history without running your own indexer, with providers like Rarible and thirdweb Insight. See [Indexers](/tooling-and-infra/indexers) for the full list and for building custom transfer indexes.

### Wallets and onboarding

Embedded wallets and account abstraction let users mint with an email or social login. With smart accounts and a paymaster you can sponsor **gasless mints**. See [Wallet infrastructure](/tooling-and-infra/wallet-infra).

### Randomness (fair mints and reveals)

For provably fair mint order and trait reveals, use a verifiable random function (VRF). See [Oracles](/tooling-and-infra/oracles).

### Token-bound accounts (ERC-6551)

The canonical Tokenbound stack (registry, account proxy, and implementation) is deployed on Monad at the same addresses as every other EVM chain, so the SDK's default flow works out of the box. See [Get started with ERC-6551](/guides/erc-6551).

### Metadata and storage

Metadata works the same as anywhere on EVM: point `tokenURI` at a stable location. For decentralized permanence, pin your files and JSON to IPFS or Arweave (for example Pinata, thirdweb Storage, or Irys) and reference them with `ipfs://` URIs. Freeze metadata once revealed so collectors can trust it will not change.

## What minting costs

Minting on Monad is efficient. A mint costs its gas usage times the network [base fee](/developer-essentials/gas-pricing) (typically about 100 gwei), paid in MON:

```
total cost (MON) = number of NFTs x gas per mint x base fee
```

Typical gas per mint:

* Gas-optimized batch mint (ERC-721A): about 40,000 gas
* Standard ERC-721 mint: about 85,000 gas

Use the estimator to size a specific drop. It computes cost in MON from the gas and base fee, which is always accurate, and can pull the live MON price for an optional USD figure.

<MintCostCalculator />

At a base fee of 100 gwei, minting out a full 10,000-item collection uses on the order of 40 to 85 MON in total gas, and deploying the contract is a one-time cost of roughly 0.25 MON. Each individual mint costs a negligible amount, which is what makes large drops, open editions, and fully onchain art practical on Monad. If you sponsor gas with a paymaster, the team covers this small amount instead of the minter.

## Resources

* [Get started with ERC-6551 (Token-Bound Accounts)](/guides/erc-6551)
* [Indexers](/tooling-and-infra/indexers)
* [Wallet infrastructure](/tooling-and-infra/wallet-infra)
* [Oracles](/tooling-and-infra/oracles)
* [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts)
* [ERC-721A](https://github.com/chiru-labs/ERC721A)

## Need help?

Join the [Monad Developer Discord](https://discord.gg/monaddev).
