迁移到 EIP-9210 并不是简单地把合约地址换掉,它会从元数据结构、事件监听和铸造流程三个层面影响现有 React 应用。如果沿用旧的 NFT 展示逻辑,前端会无法正确读取诗歌作品的标题、作者署名和内容链接,甚至在铸造时因为缺少必要字段而被合约拒绝。因此,迁移的重点在于让前端数据模型与 EIP-9210 的标准化要求对齐,同时保持 React 组件层的灵活性和可维护性。

一、理解 EIP-9210 带来的数据模型变化
EIP-9210 对诗歌 NFT 的元数据提出了比通用 ERC-721 更细粒度的要求。普通的 NFT 元数据通常只包含名称、描述和图片地址,而 EIP-9210 增加了 author、composedAt、contentURI、copyright 等字段,并且要求 contentURI 指向的链下内容必须能被稳定解析。这意味着 React 应用里原有的 NFTItem 类型定义需要扩展,不能继续沿用只有 name 和 image 的结构。
在实际迁移中,可以先在 TypeScript 或 PropTypes 中新增一个 PoemNFT 接口,把 author 设计为地址类型,composedAt 使用 Unix 时间戳,contentURI 保留 ipfs:// 前缀。这样做的好处是,后续组件在渲染诗歌卡片时可以直接从元数据中取到完整的署名和创作时间,而不用再额外发起链上查询。数据层的改造虽然看起来只是加几个字段,但它决定了整个前端能否正确展示 EIP-9210 的核心信息。
// 迁移后的诗歌 NFT 数据类型
export interface PoemNFT {
tokenId: string;
title: string;
author: string; // 以太坊地址
composedAt: number; // Unix 时间戳(秒)
contentURI: string; // ipfs:// 开头的链下内容地址
copyright: string; // 版权声明,例如 CC-BY-SA-4.0
metadataURI: string; // 完整元数据 JSON 的链上或链下地址
}
二、升级 React 应用中的合约交互层
旧的 React 应用可能还在使用 web3.js 0.x 或 ethers.js v5,它们对事件监听和 ABI 格式的处理方式与当前版本存在差异。EIP-9210 合约通常会定义 PoemMinted(uint256 indexed tokenId, address indexed author, string contentURI) 这样的事件,如果你继续使用旧版的 contract.on 写法,可能会出现事件签名不匹配或无法捕捉到铸造结果的问题。
建议将交互层统一封装到一个自定义 Hook 中,例如 useEIP9210。该 Hook 内部负责初始化 provider、连接签名者、加载 ABI,并对外暴露 readPoem 和 mintPoem 两个方法。迁移时要注意,EIP-9210 的铸造函数可能要求传入结构体参数,而不再只接收一个字符串。前端必须根据最新的 ABI 构造完整的参数对象,否则交易会静默失败或消耗额外 gas。
import { useCallback, useState } from 'react';
import { ethers } from 'ethers';
const EIP9210_ABI = [
'function mintPoem(tuple(string title, address author, uint256 composedAt, string contentURI, string copyright) data) external returns (uint256)',
'event PoemMinted(uint256 indexed tokenId, address indexed author, string contentURI)'
];
export function useEIP9210(contractAddress) {
const [isPending, setIsPending] = useState(false);
const [error, setError] = useState(null);
const mintPoem = useCallback(async (poem) => {
setIsPending(true);
setError(null);
try {
const provider = new ethers.BrowserProvider(window.ethereum);
const signer = await provider.getSigner();
const contract = new ethers.Contract(contractAddress, EIP9210_ABI, signer);
const tx = await contract.mintPoem(poem);
const receipt = await tx.wait();
setIsPending(false);
return receipt;
} catch (err) {
setError(err.message);
setIsPending(false);
throw err;
}
}, [contractAddress]);
return { mintPoem, isPending, error };
}
三、实现诗歌元数据上传与铸造组件
EIP-9210 要求 contentURI 指向一个可公开访问的链下文件,通常是 IPFS 上的 JSON 或纯文本。React 应用迁移时,需要把原来的图片上传逻辑扩展为同时上传诗歌正文和元数据。可以先使用 ipfs-http-client 或 Web3.Storage 将诗歌文本和元数据对象上传到 IPFS,拿到 ipfs:// 地址后再调用合约的 mintPoem 方法。
诗歌元数据本身也要符合 EIP-9210 的结构。它应该包含 title、author、composedAt、contentURI、copyright 等字段,并且还可以附加 language、form 等文学属性。前端在构造元数据对象时要避免把临时状态或未上链的内容写入其中,否则一旦 IPFS 内容变化,已铸造的 NFT 就会指向错误的元数据。
{
"title": "春夜",
"author": "0xAb5801a7D398351b8bE11C439e05C5B3259aeC9B",
"composedAt": 1715260800,
"contentURI": "ipfs://QmXoypizjW3WknFiJnKLwHCnL72vedxjQkDDP1mXWo6uco",
"copyright": "CC-BY-SA-4.0",
"language": "zh-CN",
"form": "五言绝句"
}
在 React 组件层面,可以编写一个 MintPoemForm 组件,接收诗歌草稿数据,先调用 IPFS 上传服务,再调用 useEIP9210 提供的 mintPoem。用户点击铸造按钮后,组件需要显示交易等待状态和错误信息。迁移时不要为了省事把上传和铸造逻辑写在 onClick 内联函数里,否则后续很难做单元测试和错误恢复。
import { createElement, useState } from 'react';
import { useEIP9210 } from './hooks/useEIP9210';
export function MintPoemForm({ contractAddress }) {
const [title, setTitle] = useState('');
const { mintPoem, isPending, error } = useEIP9210(contractAddress);
const handleMint = async () => {
const poemData = {
title,
author: window.ethereum.selectedAddress,
composedAt: Math.floor(Date.now() / 1000),
contentURI: 'ipfs://QmPlaceholder', // 实际项目中先上传再填入
copyright: 'CC-BY-SA-4.0'
};
await mintPoem(poemData);
};
return createElement('div', null,
createElement('input', { value: title, onChange: (e) => setTitle(e.target.value), placeholder: '输入诗歌标题' }),
createElement('button', { onClick: handleMint, disabled: isPending }, isPending ? '铸造中' : '铸造诗歌'),
error ? createElement('p', { style: { color: 'red' } }, error) : null
);
}
四、迁移后的测试要点与兼容性排查
迁移完成后,不能只靠手动点击来验证功能,需要针对 EIP-9210 的数据结构和合约调用补充测试。可以在测试环境中模拟 window.ethereum 对象,验证 useEIP9210 是否能正确构造交易参数,以及组件在错误状态下是否能展示清晰提示。特别是 contentURI 的格式,必须是完整的 ipfs:// 地址,不能省略为 ipfs/ 或裸哈希。
另一个容易忽略的兼容性问题是链 ID 和网络切换。EIP-9210 合约可能部署在测试网和主网,React 应用需要根据当前网络动态调整合约地址,并在用户切换钱包网络时给出提示。推荐在 useEIP9210 里增加网络检查逻辑,如果用户当前不在支持的链上,直接阻止交易并提示切换网络,避免用户支付了 gas 但交易发往错误网络。
迁移的最后一步是回归测试原有的浏览和详情页面,确认新引入的 PoemNFT 类型没有破坏旧的渲染逻辑。对于已经铸造的非 EIP-9210 作品,可以保留兼容读取回退逻辑,例如当元数据中缺少 author 字段时使用 owner 地址代替。这样既保证了新标准的上线,也不会让存量数据在迁移期间无法展示。