> ## 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.

# 使用 NFT 构建

> 如何在 Monad 上构建 NFT 项目:标准、部署快速入门,以及用于铸造、市场、索引、钱包等的工具链。

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 完全兼容 EVM,因此您已经熟悉的 NFT 技术栈可以无缝使用。它的高吞吐和低费用也让高频铸造与全链上 NFT 变得切实可行。

## 您可以构建什么

在 EVM 链上能构建的一切,再加上一些只有当区块空间既便宜又快速时才顺手的场景:

* **收藏品与 PFP**:标准 ERC-721/1155 发售、白名单以及揭晓。
* **全链上与动态 NFT**:将艺术资源或状态存储在链上,并在交互时更改元数据。
* **高频铸造**:在其他链上会成本过高的大规模系列和开放版次。
* **游戏与应用资产**:存活于链上的道具、通行证与奖励。
* **代币绑定账户**:通过 [ERC-6551](/zh/guides/erc-6551) 让每个 NFT 拥有自己的钱包。

## NFT 标准

Monad 执行标准 EVM 字节码,因此常见的标准和库都可以无缝使用:

* **ERC-721**:基础非同质化代币标准。
* **ERC-1155**:面向版次和半同质化物品的多代币标准。
* **ERC-721A**:面向便宜批量铸造、经过 gas 优化的 ERC-721。
* **EIP-2981**:链上版税信号。与在其他所有链上一样,版税由市场读取,是否强制执行取决于市场。
* **[ERC-6551](/zh/guides/erc-6551)**:代币绑定账户,让每个 NFT 都能拥有资产、与合约交互并维持自己的链上身份。

一些流行的实现:[OpenZeppelin](https://docs.openzeppelin.com/contracts)、[thirdweb](https://portal.thirdweb.com/contracts)、[solady](https://github.com/Vectorized/solady) 或 [ERC721A](https://github.com/chiru-labs/ERC721A)。

实验性原语包括 [ERC-404](https://github.com/Pandora-Labs-Org/erc404)(非官方的混合代币混合体)和 [DN-404](https://github.com/Vectorized/dn404)(相互关联的 ERC-20/721 对)。两者可部署、面向 Monad 配置的示例见 [monad-developers/erc404-dn404-monad](https://github.com/monad-developers/erc404-dn404-monad)。

## 快速入门:部署一个系列

使用 [Foundry](/zh/tooling-and-infra/toolkits/foundry) 将一个最小的 ERC-721 部署到 Monad 测试网。

**先决条件:** 已安装 Foundry,并有一个已获得测试网资金的账户。Monad 测试网的 chain ID 是 `10143`,主网是 `143`。请从 [测试网](/zh/developer-essentials/testnet) 页面获取测试网资金和 RPC 端点。

搭建项目并添加 OpenZeppelin:

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

编写合约:

```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/";
    }
}
```

部署它:

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

<Note>
  在部署之前请为部署账户注资。请从 [测试网](/zh/developer-essentials/testnet) 页面链接的水龙头获取测试网 MON。
</Note>

然后铸造第一个 token:

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

以上就是一个可工作的系列。在此基础上,您可以换成白名单、公开铸造价格,或采用 ERC-721A 这样的批量铸造标准,并接入下方的工具链。

## 选择您的工具链

NFT 项目所需的一切在 Monad 上都已上线。可按层次逐项挑选。

### 铸造与 launchpad

用于创建、部署和管理系列的无代码工具:

* **[Scatter](https://www.scatter.art/)**:以艺术家为先的 launchpad,通过低费用的合约工厂部署完全归属项目方的 ERC-721A/1155 系列。
* **[thirdweb](https://thirdweb.com)**:预置的 Drop 合约和领取 UI。

### 市场

在 Monad 上运行的市场中挂单和交易:

* **[OpenSea](https://opensea.io)**:包括用于一级铸造的 SeaDrop。
* **[Scatter](https://www.scatter.art/)**:买卖各类系列,与其他 NFT 市场兼容。

### 索引与 NFT API

无需运行您自己的索引器即可读取余额、所有权、元数据和转账历史,提供方例如 Rarible 和 thirdweb Insight。完整列表以及自建转账索引的方法,请参见 [索引器](/zh/tooling-and-infra/indexers)。

### 钱包与用户引导

嵌入式钱包与账户抽象让用户通过邮箱或社交登录即可完成铸造。结合智能账户和 paymaster,您可以赞助 **无 gas 铸造**。请参见 [钱包基础设施](/zh/tooling-and-infra/wallet-infra)。

### 随机性(公平铸造与揭晓)

如需可验证公平的铸造顺序和特征揭晓,请使用可验证随机函数(VRF)。请参见 [预言机](/zh/tooling-and-infra/oracles)。

### 代币绑定账户(ERC-6551)

规范的 Tokenbound 技术栈(registry、账户 proxy 以及实现)在 Monad 上部署的地址与其他所有 EVM 链一致,因此 SDK 的默认流程开箱即用。请参见 [开始使用 ERC-6551](/zh/guides/erc-6551)。

### 元数据与存储

元数据在 EVM 上的用法始终一致:将 `tokenURI` 指向一个稳定的位置。为了实现去中心化的持久化,请将文件和 JSON 固定(pin)到 IPFS 或 Arweave(例如 Pinata、thirdweb Storage 或 Irys),并用 `ipfs://` URI 引用它们。揭晓完成后请冻结元数据,以便收藏者相信它不会再变。

## 铸造的成本

在 Monad 上铸造效率很高。一次铸造的成本等于其 gas 用量乘以网络 [base fee](/zh/developer-essentials/gas-pricing)(通常约 100 gwei),以 MON 支付:

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

每次铸造的典型 gas 用量:

* 经过 gas 优化的批量铸造(ERC-721A):约 40,000 gas
* 标准 ERC-721 铸造:约 85,000 gas

使用下方的估算器来估计具体一次发售的规模。它会用 gas 与 base fee 计算 MON 成本(这总是准确的),并可以拉取实时的 MON 价格得出一个可选的美元数字。

<MintCostCalculator />

当 base fee 为 100 gwei 时,铸造完一整个 10,000 个的系列大约总共消耗 40 到 85 MON 的 gas,而部署合约是一次性成本,约 0.25 MON。每一次单独的铸造花费都可以忽略,这正是让大规模发售、开放版次和全链上艺术在 Monad 上切实可行的原因。如果您通过 paymaster 赞助 gas,团队而不是铸造者承担这笔小额费用。

## 资源

* [开始使用 ERC-6551(代币绑定账户)](/zh/guides/erc-6551)
* [索引器](/zh/tooling-and-infra/indexers)
* [钱包基础设施](/zh/tooling-and-infra/wallet-infra)
* [预言机](/zh/tooling-and-infra/oracles)
* [OpenZeppelin Contracts](https://docs.openzeppelin.com/contracts)
* [ERC-721A](https://github.com/chiru-labs/ERC721A)

## 需要帮助?

加入 [Monad 开发者 Discord](https://discord.gg/monaddev)。
