1 背景

2022 年是 Web3 行业很不如意的一年,DeFi 旧力已去,游戏和社交应用新力未发,放眼望去,似乎只有 NFT 仍然热度不减,各种应用、各种概念也是层出不穷,几乎是独力支撑熊市当中 Web3 的希望。除了围绕 NFT 艺术品层出不穷的新玩法之外,过去一年至少有两项基础创新值得关注:

第一是 Chainlink 提出的 dNFT (动态 NFT),即发布之后还能够响应外部事件而变化的 NFT。

第二是 Solv Protocol 提出的 ERC-3525 半匀质化通证(SFT),不但具有动态变化的能力,而且像 ERC-20 一样可以计算,像账户一样能够接受、存储、发送和编程数字资产,特别适合表达复杂的数字资产,如金融票据、积分卡、真实世界资产等等。

无论是 dNFT 还是 SFT,相比于原始的 NFT,可以动态变化的图像都是一个突出的亮点。我们知道,目前艺术类 NFT 的主要价值就在于其图像外观。而此类 NFT 的图像大都是每 ID 或者每品类固定,在 NFT 创造之初就已经确定,一旦确定就终生不能修改。这种业务逻辑之下,我们并不需要更复杂的机制,简单的静态图像就可以满足需求。而面向复杂应用的 NFT 则不然,如果 NFT 的图像能根据业务流程和外部事件“动起来”,那么无疑将会大大提升 NFT 的应用价值和用户体验。这种可以动态 NFT,我们称之为数据驱动的动态 NFT,或数据驱动的 dNFT,以区别于由时间轴驱动的、简单重复的动画 NFT。

以 Uniswap V3 为例,其交易对 LP NFT 是一种金融 NFT,定义和展现了一个 LP 的作市策略和资金头寸。由于 Uniswap V3 在开发时并没有成熟的 dNFT 解决方案,ERC-3525 更是还需要一年半才会问世,因此其开发团队所实现的 NFT 仍然是静态的、固定的,并不能够动态地显示用户的所有者、当前头寸、当前价格、无常损失等动态数据,可以说是 Uniswap 团队由于 NFT 技术限制而在产品层面进行了的妥协。如果当时存在这样的技术,NFT 的图像可以响应底层数据的变化而发生变化,向 Uniswap 的 LP 提供丰富的实时信息,那么无疑会大大提升 Uniswap V3 LP NFT 的信息透明度和用户体验。这就是典型的数据驱动的 dNFT。请注意,这里的动态图像并不是简单的时间轴动画,而是数据驱动的、可响应外部事件而变化的图像。

本文介绍了一种实现数据驱动的 dNFT 的方案,我们在实践中已经部署了这个方案,经验证非常有效,且能够与主流 NFT 平台对接。

2. 方案

我相信,大家对 NFT 已经非常熟悉了,其核心属性也耳熟能详。token id、token URI、owner,其中token URI 描述 NFT 的扩展属性,正是其构建复杂应用的核心字段。该字段也被当前主流的 NFT 交易平台 Opensea 所扩展,并被各方广泛认可。我们的生成动态NFT图像的方案也正是围绕该字段构建。

试想一下,有如下需求:在 Uniswap V3 交易对NFT上展示owner及头寸(position)信息。

我们会面临哪些问题?有什么样的解决方案呢?

在Uniswap V3 交易对 NFT 图像中,我们面临的问题就是如何生成动态 NFT 图像。LP 易主或者头寸产生变动时,都会驱动图像的刷新。

怎么开发这样的 NFT 呢?基本思路是,按需动态渲染生成 NFT 图像,在访问时实时渲染生成图像。

展开来描述。目前Uniswap V3 交易对 NFT 图像只展示静态数据,我们的目标是增加 Owner (所有者)和 Position (头寸)的 LP NFT 图像,我们可以把图像和数据分解开来:

  • 底图 —— NFT 图像的整体架构

  • 数据 —— NFT 图像上的可变数据

因此,我们生成动态NFT图像的方式是:以一个固定的底图为模板,在用户访问时基于数据实时生成图像的动态部分。

2.1 底图

  • 底图配置(索引)

其中,可以根据合约地址配置,一个合约发出的所有 dNFT 共用同一个底图。也可以基于某一个属性动态选择底图,比如,在 Uniswap V3 中可以根据动态收益率来选择不同的底图,收益率越高,底图越喜庆。

contract address → background image Acontract address + 0.5% → background image 1contract address + 1% → background image 2
  • 底图格式

底图格式决定图像生成的技术,我们的最佳实践是 SVG 格式。SVG 格式底图模板示例如下:

<svg width="600px" height="400px" viewBox="0 0 600 400" version="1.1" xmlns="" xmlns:xlink="">.text {pointer-events: none;-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none; font-variant-numeric: tabular-nums lining-nums; }Owner: {{OwnerPlaceholder}}USDC: {{USDCPlaceholder}}WETH: {{WETHlaceholder}}

基于 SVG 底图模板,获取NFT相关数据之后,只需用字符串模板工具替换相应 Placeholder 即可生成图像。

2.2 业务数据

获取业务数据的方式取决于 NFT 应用的实现方式。一个常用的模式是调用合约 tokenURI 接口,将返回结果基于业务解析即可获取业务数据。下面介绍这种模式的具体做法。

以下是 ERC-721 的元数据接口:

interface ERC721Metadata /* is ERC721 */ {/// @notice A descriptive name for a collection of NFTs in this contractfunction name() external view returns (string _name);/// @notice An abbreviated name for NFTs in this contractfunction symbol() external view returns (string _symbol);/// @notice A distinct Uniform Resource Identifier (URI) for a given asset./// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC///3986. The URI may point to a JSON file that conforms to the "ERC721///Metadata JSON Schema".function tokenURI(uint256 _tokenId) external view returns (string);}

这个接口中的 tokenURI() 函数可以返回一个 URI,指向该 NFT 对应的元数据资源在网络上的位置。对于一个实现了 ERC721Metadata 接口的 NFT,我们可以调用 tokenURI() 接口,获得该 NFT 的元数据。

ERC-721 标准对于元数据结构进行了以下建议:

{"title": "Asset Metadata","type": "object","properties": {"name": {"type": "string","description": "Identifies the asset to which this NFT represents"},"description": {"type": "string","description": "Describes the asset to which this NFT represents"},"image": {"type": "string","description": "A URI pointing to a resource with mime type image/* representing the asset to which this NFT represents. Consider making any images at a width between 320 and 1080 pixels and aspect ratio between 1.91:1 and 4:5 inclusive."}}}

很显然,关于这个 NFT 的动态数据,应该放在元数据中,以便被读取。

不过 NFT 的元数据结构并非一定,标准所规定的的格式也未必是最合理。开发者完全可以基于应用的具体情况来定义自己的元数据结构。

例如,OpenSea 就建议了另一个元数据的结构,以下是一个例子,具体建议请参见:https://docs.opensea.io/docs/metadata-standards

{"description": "Friendly OpenSea Creature that enjoys long swims in the ocean.", "external_url": "", "image": "", "name": "Dave Starbelly","attributes": [ ... ]}

不管用哪种元数据结构,开发者都必须清楚地定义这种结构,并且将数据放在这个结构中,供 DAPP 调用。DAPP 读取元数据后,通过解析获得

2.3 实现方案

在当前技术条件下,底图结合动态数据驱动实时渲染生成动态 NFT 图像,还需要部署一个中心化服务来配合 DAPP 才能实现。前两节已经介绍了底图的定义方式和 NFT 应用业务数据的获取模式。这个方案的大致处理流程如下:

  • 首先,DAPP(或其他Client) 向 Image Handler 发送一个 image URI 请求获得对应的图像;

  • Image Handler 是一个动态图像服务,收到 image调用后,向 RPC Node 发起请求,调用相应 NFT 合约的 tokenURI 函数获得 NFT metadata;同时,Image Handler 到指定位置获得底图模板代码;

  • 获取 metadata 后,根据相关 NFT 底图模板和 metadata 中的数据,替换底图 NFT 模板中的 Placeholders,从而实时生成 SFT 图像,这一过程也称为“渲染”:

{"name": "Image Handler Example","description": "This is an image handler example.", "image": "image URI", }

其中 image 字段即为特定 image URI,其路由形式可以灵活设计,例如:

这个解决方案的重点是,Image Handler 必须能够根据这个 URI 顺利地同时找到所需的底图和数据资源,然后将两者结合起来实时渲染 SVG 图像,从而展示出来。

3 小结

随着复杂 NFT 应用的不断涌现,生成数据驱动的动态 NFT 的需求将会越来越多,以上只是可行方案之一,希望能够抛砖引玉。值得一提的是,ERC-3525 标准对于数据驱动的动态 SFT 提供了更高水平的支持,以后我们将介绍基于 ERC-3525 的数据驱动、实时渲染的动态 SFT 生成方案。