前言
本文将针对ERC721、ERC721A与ERC721C这三个标准,从核心差异、关键机制、选型建议等多个维度进行系统梳理,并通过代码实现给出直观的对比分析。补充说明关于ERC721和ERC721A智能合约的开发测试部署可以参考博主的另外两篇文章《快速实现一个标准的NFT合约(实操篇)》和《ERC721与ERC721A:NFT标准的对比与特性分析》,本文着重对ERC721C合约的实现;
概述
ERC721 是 NFT 基础标准,ERC721A 是其批量铸造的 Gas 优化版,ERC721C 则聚焦创作者版税强制与转移管控,三者在核心目标、实现机制与适用场景上差异显著。当然了三个标准并非简单替代关系,而是针对不同痛点的专业化解决方案;
核心差异对比表
| 维度 | ERC721(OpenZeppelin) | ERC721A(Azuki) | ERC721C(Creator Token) |
|---|---|---|---|
| 定位 | NFT 基础标准 | 批量铸造 Gas 优化实现 | 创作者版税与转移管控增强 |
| 数据结构 | 每个 tokenId 独立存储所有者 | 连续 ID 区间共享所有者(惰性初始化) | 基于 ERC721 扩展转移限制与支付处理器 |
| 批量铸造 Gas | 线性增长(10 个≈62 万) | 近似常数(10 个≈14 万,优化 77%) | 与 ERC721 相当,额外版税逻辑略增 |
| 首次转移成本 | 稳定(≈3.5 万) | 略高(补齐存储),后续转移正常 | 随管控级别变化 |
| 核心特性 | 所有权与转移基础接口 | 批量铸造、低成本分发 | 强制版税、平台白名单、转移规则 |
| 兼容市场 | 全兼容 | 主流兼容 | 依赖支持 CAPS 的市场(OpenSea、Magic Eden) |
| 适用场景 | 通用 NFT、单枚铸造 | 大型集合、批量空投 | 创作者经济、IP 管控、版税依赖项目 |
关键机制解析
-
ERC721(基础标准)
- 定义 NFT 核心接口(ownerOf、transferFrom 等),每个 tokenId 独立记录所有者,逻辑清晰但批量操作 Gas 成本高。
- 适合单枚铸造、小体量收藏或需要高度自定义的项目。
-
ERC721A(Gas 优化)
- 通过 “区间所有者” 模型,连续 tokenId 仅在起始位置记录所有者,后续 ID 通过向前查找确定归属。
- 批量铸造成本近乎固定,适合 10000 + 规模 PFP、游戏道具空投等高频分发场景。
- 注意:首次转移非连续 ID 时需补齐存储,Gas 略高于 ERC721。
-
ERC721C(版税与管控)
- 引入 PaymentProcessor 合约强制版税,通过_beforeTokenTransfer 钩子限制非白名单平台交易。
- 支持创作者设置转移规则(如禁止 P2P、最低售价),并可与市场分润激励推广。
- 适合依赖版税收入的艺术家、IP 方及需要严格转移管控的项目。
选型建议
- 优先选ERC721:通用 NFT、小批量铸造、追求最大兼容性。
- 优先选ERC721A:大型集合、批量分发、对 Gas 成本敏感。
- 优先选ERC721C:创作者经济、IP 保护、需强制版税与转移管控。
智能合约开发、测试、部署
智能合约
1.ERC721智能合约:
关于ERC721智能合约可以查看博主的此篇文章《快速实现一个标准的NFT合约(实操篇)》;包含了完整的开发、测试、部署全流程,相关内容就不赘述了;
2.ERC721A智能合约
关于ERC721A智能合约可以查看博主的此篇文章《ERC721与ERC721A:NFT标准的对比与特性分析》;包含了完整的开发、测试、部署全流程,相关内容就不赘述了;
3.ERC721C智能合约
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;
/*
* 依赖:
* @openzeppelin/contracts@5.4.0
* solady@^0.1.26
*/
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {ERC2981} from "@openzeppelin/contracts/token/common/ERC2981.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {LibString} from "solady/src/utils/LibString.sol";
/**
* @title ERC721C
* @notice 极简 ERC-721 + ERC-2981 实现,使用 solady 优化字符串与签名
*/
contract ERC721C is ERC721, ERC2981, Ownable {
using LibString for uint256;
/*//////////////////////////////////////////////////////////////
CONFIG
//////////////////////////////////////////////////////////////*/
uint96 private constant _DEFAULT_ROYALTY_BPS = 500; // 5%
string private _baseTokenURI;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/
constructor(
string memory name_,
string memory symbol_,
string memory baseURI_,
address royaltyReceiver_
) ERC721(name_, symbol_) Ownable(_msgSender()) {
_baseTokenURI = baseURI_;
_setDefaultRoyalty(royaltyReceiver_, _DEFAULT_ROYALTY_BPS);
}
/*//////////////////////////////////////////////////////////////
PUBLIC / EXTERNAL
//////////////////////////////////////////////////////////////*/
/// @dev 安全铸造(仅合约拥有者)
function safeMint(address to, uint256 tokenId) external onlyOwner {
_safeMint(to, tokenId);
}
/// @dev 批量安全铸造(仅合约拥有者)
function safeBatchMint(address to, uint256[] calldata tokenIds) external onlyOwner {
for (uint256 i; i < tokenIds.length; ) {
_safeMint(to, tokenIds[i]);
unchecked { ++i; }
}
}
/// @notice 更新 baseURI
function setBaseURI(string calldata newBaseURI) external onlyOwner {
_baseTokenURI = newBaseURI;
}
/// @notice 更新版税(ERC-2981)
function setRoyaltyInfo(address receiver, uint96 feeBps) external onlyOwner {
_setDefaultRoyalty(receiver, feeBps);
}
/*//////////////////////////////////////////////////////////////
INTERNAL / VIEW
//////////////////////////////////////////////////////////////*/
function _baseURI() internal view override returns (string memory) {
return _baseTokenURI;
}
/// @dev 统一授权接口支持(ERC-721 + ERC-2981)
function supportsInterface(bytes4 interfaceId)
public
view
override(ERC721, ERC2981)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
}
编译指令
npx hardhat compile
智能合约部署脚本
注释:采用HardhatV3中viem 或者ethers.js同理语法略有不同,关于ERC721和ERC721A部署详情可以参考以上对用的文章
部署脚本
// scripts/deploy.js
import { network, artifacts } from "hardhat";
async function main() {
// 连接网络
const { viem } = await network.connect({ network: network.name });//指定网络进行链接
// 获取客户端
const [deployer] = await viem.getWalletClients();
const publicClient = await viem.getPublicClient();
const deployerAddress = deployer.account.address;
console.log("部署者的地址:", deployerAddress);
// 加载合约
const artifact = await artifacts.readArtifact("ERC721C");
const ipfsjsonuri="https://zygomorphic-magenta-bobolink.myfilebase.com/ipfs/QmQT8VpmWQVhUhoDCEK1mdHXaFaJ3KawkRxHm96GUhrXLB"
// 部署(构造函数参数:recipient, initialOwner)
const hash = await deployer.deployContract({
abi: artifact.abi,//获取abi
bytecode: artifact.bytecode,//硬编码
args: ["MyRoyaltyNFT","MRNFT",ipfsjsonuri,deployerAddress],//nft名称,nft符号,ipfsjsonuri,部署者地址
});
// 等待确认并打印地址
const receipt = await publicClient.waitForTransactionReceipt({ hash });
console.log("合约地址:", receipt.contractAddress);
}
main().catch(console.error);
部署指令
npx hardhat run ./scripts/xxx.ts
智能合约测试脚本
注释:关于ERC721和ERC721A智能合约测试详情参考以上对应的两篇完整
import assert from "node:assert/strict";
import { describe, it,beforeEach } from "node:test";
import { formatEther,parseEther } from 'viem'
import { network } from "hardhat";
describe("ERC721C", async function () {
let viem: any;
let publicClient: any;
let owner: any, user1: any, user2: any, user3: any;
let deployerAddress: string;
let MyERC721C: any;
beforeEach (async function () {
const { viem } = await network.connect();
publicClient = await viem.getPublicClient();//创建一个公共客户端实例用于读取链上数据(无需私钥签名)。
[owner,user1,user2,user3] = await viem.getWalletClients();//获取第一个钱包客户端 写入联合交易
deployerAddress = owner.account.address;//钱包地址
const ipfsjsonuri="https://zygomorphic-magenta-bobolink.myfilebase.com/ipfs/QmQT8VpmWQVhUhoDCEK1mdHXaFaJ3KawkRxHm96GUhrXLB";
MyERC721C = await viem.deployContract("ERC721C", [
"My Royalty NFT",
"MRNFT",
ipfsjsonuri,
deployerAddress
]);//部署合约
console.log("MyRoyaltyNFT合约地址:", MyERC721C.address);
});
it("测试ERC721C", async function () {
//查询nft名称和符号
const name= await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "name",
args: [],
});
const symbol= await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "symbol",
args: [],
});
//查询合约拥有者
const ownerAddress= await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "owner",
args: [],
});
console.log(name,symbol,ownerAddress)
//铸造单个nft
const safeMintHash=await owner.writeContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "safeMint",
args: [user1.account.address,0],
});
console.log("铸造单个nft交易哈希:",`${safeMintHash} eth`)
//等待交易确认
const receipt = await publicClient.getTransactionReceipt({
hash: safeMintHash,
});
console.log("铸造单个nft交易确认gas:",formatEther(receipt.gasUsed))
//批量铸造nft
const nftIds=[1,2,3]
const safeBatchMinthash =await owner.writeContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "safeBatchMint",
args: [user1.account.address,nftIds],
});
//估算gas费用
console.log("批量铸造nft交易哈希gas:",`${safeBatchMinthash} eth`)
//等待交易确认
const receipt1 = await publicClient.getTransactionReceipt({
hash: safeBatchMinthash,
});
console.log("批量铸造nft交易确认:",formatEther(receipt1.gasUsed))
//查询单个nft的tokenURI
const TokenURI= await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "tokenURI",
args: [0],
});
console.log(TokenURI)
//查询余额和拥有者
const balanceOf=await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "balanceOf",
args: [user1.account.address],
});
console.log(balanceOf)
//查询nft的拥有者
const ownerOf=await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "ownerOf",
args: [0],
});
console.log(ownerOf)
//查询版税信息
const royaltyInfo=await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "royaltyInfo",
args: [0,parseEther("2")],
});
console.log(royaltyInfo)
const GETAPPROVED=await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "getApproved",
args: [0],
});
console.log(GETAPPROVED)
//设置BaseURI
const ipfsjsonuri1="https://zygomorphic-magenta-bobolink.myfilebase.com/ipfs/QmcN49MKt4MbSXSGckAcpvFqtea43uuPD2tvmuER1mG67s";
const setBaseURI=await owner.writeContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "setBaseURI",
args: [ipfsjsonuri1],
});
console.log(setBaseURI)
//查询更新后的tokenURI
const TokenURI1= await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "tokenURI",
args: [0],
});
console.log("更新后",TokenURI1)
//设置默认版税
// const SETDEFAULTROYALTY=await owner.writeContract({
// address: MyRoyaltyNFT.address,
// abi: MyRoyaltyNFT.abi,
// functionName: "setDefaultRoyalty",
// args: [user3.account.address,"500"],
// });
// console.log(SETDEFAULTROYALTY)
//设置版税
const setRoyaltyInfo = await owner.writeContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "setRoyaltyInfo",
args: [user3.account.address,"200"],
});
//查询版税信息
const royaltyInfo1=await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "royaltyInfo",
args: [0,parseEther("3")],
});
console.log("更新后版税信息",royaltyInfo1)
//转账nft
const TRANSFERFROM=await user1.writeContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "transferFrom",
args: [user1.account.address,user2.account.address,0],
});
//查询nft的新拥有者
const ownerOf1=await publicClient.readContract({
address: MyERC721C.address,
abi: MyERC721C.abi,
functionName: "ownerOf",
args: [0],
});
console.log(ownerOf1)
});
});
测试指令
npx hardhat test ./test/xxx.ts
总结
至此,关于 ERC721、ERC721A、ERC721C 三类 NFT 标准合约的功能特性、适用场景对比,以及基于 Hardhat V3 与 Viem 框架的开发、测试、部署全流程工作 已全部完成。