EIP9490是一份面向大规模图像类NFT的元数据扩展标准,它把链上存储的职责收敛到指纹与路由信息,把图像本体交给内容寻址网络。天文摄影NFT因为单张图片动辄几十MB的RAW或TIFF文件,对这套架构的需求尤其迫切。Telescope则是一个面向天文数据的索引与解析服务,能够将EIP9490描述的观测元数据(望远镜型号、曝光参数、天体坐标)转成结构化JSON供前端直接消费。这篇文章以一个React项目为例,完整走一遍迁移流程。

EIP9490的核心设计与迁移前的准备
EIP9490的元数据结构分为三层:第一层是链上的tokenURI入口,只保存一个内容哈希和路由前缀;第二层是描述文件,包含图像的多分辨率索引和校验值;第三层才是实际的资产文件。这种分层的好处是,链上写入成本从原来的几十美元级别降到几美分,因为描述文件本身非常小。
迁移前的第一步是审计现有合约的tokenURI实现。如果你的旧合约把Base64编码的图像直接写进了元数据,那么需要先用脚本把这些数据导出成独立文件,计算SHA-256哈希,再按EIP9490的格式重新组织描述文件。下面是一个描述文件的示例结构:
{
"eip9490": {
"version": "1.0",
"asset": {
"full": "ipfs://bafy.../nebula_raw.tif",
"sha256": "9f2c8a...",
"size": 48210304
},
"variants": [
{"name": "preview", "width": 512, "uri": "ipfs://bafy.../nebula_512.webp"},
{"name": "display", "width": 2048, "uri": "ipfs://bafy.../nebula_2048.webp"}
]
},
"astronomy": {
"telescope": "Celestron RASA 11",
"exposure": "300x120s",
"coordinates": {"ra": "05h35m17s", "dec": "-05h23m28s"}
}
}注意astronomy字段是EIP9490留给垂直领域扩展的命名空间,Telescope正是通过这个字段来识别天文类资产并建立索引。准备阶段还应该确认所有历史资产都能重新生成描述文件,一旦合约地址切换,老tokenId必须能映射到新的元数据入口。
Telescope服务的接入与数据流改造
Telescope提供REST和GraphQL两种接口,针对React应用推荐GraphQL,因为NFT详情页通常需要一次性拉取资产变体、观测参数、索引状态等多个维度的数据。接入时先在项目中安装客户端依赖,然后封装一个数据层模块,把Telescope返回的原始数据转换成组件需要的形状。
// services/telescope.js
const QUERY = `
query Asset($hash: String!) {
asset(contentHash: $hash) {
indexed
variants { name width uri }
observation {
telescope
exposure
rightAscension
declination
}
}
}
`;
export async function fetchAstroAsset(contentHash) {
const res = await fetch('https://api.telescope.example.org/graphql', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
query: QUERY,
variables: { hash: contentHash }
})
});
const { data, errors } = await res.json();
if (errors) throw new Error(errors[0].message);
return data.asset;
}这个封装层是整个迁移中最值得投入的部分。旧应用的数据获取逻辑散落在各个组件里,直接改动会牵一发而动全身。统一收口到services目录后,组件只面对干净的领域对象,底层从旧的IPFS直连切换到Telescope,组件代码几乎零改动。
另一个容易被忽视的细节是indexed字段。Telescope对资产的索引是异步的,新铸造的NFT可能在几分钟内查不到观测参数。前端必须处理这种中间态,推荐的做法是在详情页展示骨架屏,并用轮询或WebSocket等待索引完成,而不是直接渲染空数据。
React组件改造与性能优化
组件层面的改造重点是图片加载策略。EIP9490的多分辨率变体设计天然适合渐进式加载:列表页用512宽度的preview变体,详情页先加载display变体,用户点击放大时才请求full原始文件。可以封装一个AstroImage组件来固化这套逻辑:
function AstroImage({ asset, mode = 'display', onZoom }) {
const uri = asset.variants.find(v => v.name === mode)?.uri
?? asset.variants[asset.variants.length - 1].uri;
const [loaded, setLoaded] = useState(false);
return (
<div className="astro-frame">
{!loaded && <div className="astro-skeleton" />}
<img
src={uri}
loading="lazy"
decoding="async"
onLoad={() => setLoaded(true)}
onClick={() => onZoom?.(asset.asset.full)}
alt="astronomy photograph"
/>
</div>
);
}除了加载策略,校验也是迁移后需要补上的环节。EIP9490的描述文件携带SHA-256哈希,前端可以在图像下载完成后做本地校验,把结果展示在详情页上。这对天文摄影NFT尤其重要,因为买家关心的是原始 FITS 或 TIFF 文件没有被篡改。校验可以用SubtleCrypto实现,注意大文件要按块读取避免内存溢出。
最后是合约交互层。如果合约本身需要升级,建议采用代理合约模式,把新的EIP9490元数据入口指向新的实现合约,前端只需更新ABI中的tokenURI解析逻辑。迁移完成后,用几个历史tokenId做回归测试,确认老资产在新链路上的展示、校验、放大查看全链路正常,再逐步放量。
整体来看,这次迁移的核心思路是让链上只承担轻量的路由职责,重数据交给内容寻址网络,结构化解析交给Telescope这样的专业索引服务,React端则专注于渐进式加载与可信校验。分层清晰之后,后续再扩展星表数据或卫星图像等新资产类型,也只是描述文件里多一个命名空间的事。