距离上一次 NFT MetadataView 标准发布以来, 历时 11 个月, Flow 同质化代币也迎来了 MetadataView 的标准, 虽然还是在测试阶段, 但可以期待标准的正式采用, 这次的升级不仅为我们带来了同质化代币的 MetadataView 标准合约, FungibleTokenSwitchboard 可以实现一个通用的收款 Receiver 资源解决之前 NFTMetadataView 中 Royalty 配置的问题.
这次的升级也是以对标准合约低修改的方式进行, 整体的设计思路与 NFTMetadataViews 一致, 对 NFTMetadataViews 感兴趣的小伙伴可以在这里阅读我之前写的介绍文章, 还有一篇是关于 Flow NFT 与 FT 的设计实践与说明, 本文会根据 FT 标准合约的两个重大的更新进行介绍和说明.
要点:
- 本次的更新升级只部署在测试网, 新的特性需要新版本的 Cadence 支持
- 原有的 FT 合约不强制升级, 不升级不会影响使用
- 用户已经初始化过的 Vault 的资源也不需要重新初始化, 如果合约实现进行函数升级, 则用户已经初始化的资源会自动继承
- 除了包含基础的 FT 信息之外, 也会包含资源信息和类型信息(猜测是为了链上信息聚合展示做准备)
- 同 NFTMetadataView 类似, FTMetadataView 也具备展示类型扩展的能力, 可以支持未来更多的 Metadata 种类.
- FungibleTokenSwitchboard 作为一个额外的路由资源, 保存了用户账户下的 FT Receiver 资源, 能够作为一个路由通过一个资源接收多种 FT token
FungibleTokenMetadataViews
FungibleTokenMetadataViews.cdc 依赖了 FungibleToken 与 MetadataViews
其中定义了
FTView
结构, 包含了 FTDisplay
和 FTVaultData
两个结构- FTDisplay 包含了 FT 展示所需要的信息, 名称, 符号, 描述, 外部链接, Logo 链接和一些社交账号的链接, 可以理解为 Token 的基本信息
- FTVaultData 这是包含了 FT Vault 的资源信息和接口的一些类型信息,这里还包含了一个创建 Vault 的初始化函数
createEmptyVault
从实现来看
FTDisplay
是为了丰富 FungibleToken
的链上基础信息而设计, 而 FTVaultData
这是为了方便第三方集成对 Token 做链上集成而设计, 通过标准的 MetadataViews
接口就可以兼容各种类型的 FungibleToken
pub fun getViews(): [Type] { return [] } pub fun resolveView(_ view: Type): AnyStruct? { return nil }
其实
FungibleToken
只是在原有的 Balance
类型的接口中添加了 getViews
和 resolveView
两个函数, 返回的是 optional 的类型.
这里并没有引入新添加的合约依赖, 反而是在示例的 ExampleToken 中引入:注意: 这里 FungibleToken 的改动略有不同.因为增加了 Balance 接口类型中的实现函数, 会导致现有部署在网络中继承了 FungibleToken.Balance 的资源出现问题, (正常来讲这样继承了 FungibleToken.Balance 却没有实现 getViews 和 resolveView 的合约是无法正常部署的)在和 B.T.Wood 讨论时候他告诉我了一个 Cadecne 最近的更新 概括来说是如果继承了接口的资源即使没有实现接口定义的方法,这会使用接口自身的默认方法, 这样就不会出现上述问题.这个 feature 将会在未来的 spork 更新.
并在 Vault 资源里实现了这两个新的接口:
pub fun getViews(): [Type]{ return [Type<FungibleTokenMetadataViews.FTView>(), Type<FungibleTokenMetadataViews.FTDisplay>(), Type<FungibleTokenMetadataViews.FTVaultData>()] }
这里定义了三种结构类型 FTView 和他其中的两个包含的结构 FTDisplay 与 FTVaultData
升级之后我们就可以在现有的 FungibleToken 的 Balance 接口中直接调用 getViews 去获取当前 Token 支持的 MetadataView 类型, 并通过resolveView 获取具体的 Metadata 信息.
这样的代码结构, 达到了向后兼容的效果, 也满足了未来根据需要动态增加 Metadata 类型的需求, 可以通过扩展 FungibleTokenMetadataViews 支持的类型来丰富 FT 的链上元数据.
FungibleTokenSwitchboard
我们可以将
FungibleTokenSwitchboard
理解为一个账户中的聚合路由资源, 他实现了 FungibleToken.Receiver
的接口, 在标准中充当了一个万能收款的角色, 通过资源中的 receiverCapabilities
来保存用户账户下的 Vault
的 Receiver
实现了只需要一个 Receiver
就能将收款转发至对应具体的 Receiver
的功能.Switchboard
资源里定义了 receiverCapabilities
来聚合 FungibleToken
的收款资源:pub resource Switchboard: FungibleToken.Receiver, SwitchboardPublic { access(contract) var receiverCapabilities: {Type: Capability<&{FungibleToken.Receiver}>} //.... }
receiverCapabilities
以 Type
为键, Vault
的收款 Capability
为值作为路由收款的核心同时提供了管理路由的方法:
- addNewVault 支持通过
Capability
添加新的Vault
路由到Switchboard
- addNewVaultsByPath 通过资源路径的数组批量添加到
Switchboard
- removeVault 移除
因为
Switchboard
继承了 FungibleToken.Receiver
所以需要实现 deposit
函数pub fun deposit(from: @FungibleToken.Vault) { // Get the capability from the ones stored at the switchboard let depositedVaultCapability = self .receiverCapabilities[from.getType()] ?? panic ("The deposited vault is not available on this switchbard") // Borrow the reference to the desired vault let vaultRef = depositedVaultCapability.borrow() ?? panic ("Can not borrow a reference to the the vault") vaultRef.deposit(from: <-from) }
这个充值函数实现了一个转发存款的功能, 将收款路由至配置中的具体资源, 我们也看到这里如果没有找到
receiverCapabilities
对应的值, 程序就会 Panic 导致 deposit 的交易失败.为了方便第三方使用, 同时定义了
SwitchboardPublic
的接口类型定义了三种函数:getVaultTypes
获取支持接收的Vault
类型
- deposit 同上
safeDeposit
安全的充值函数, 即使失败也不会引起 Panic 而是返回@FungibleToken.Vault
资源- 和
Deposit
相同, 接收@FungibleToken.Vault
资源 - 会先做判断
receiverCapabilities
是否有配置对应资源的接收资源 - 如果没找到且充值金额大于零, 则触发充值失败的事件, 并将充值的
@FungibleToken.Vault
资源返回 - 否则直接销毁充值资源并返回
nil
pub fun safeDeposit(from: @FungibleToken.Vault): @FungibleToken.Vault? { // Try to get the proper vault capability from the switchboard // If the desired vault is present on the switchboard... if let depositedVaultCapability = self .receiverCapabilities[from.getType()] { // We try to borrow a reference to the vault from the capability // If we can borrow a reference to the vault... if let vaultRef = depositedVaultCapability.borrow() { // We deposit the funds on said vault vaultRef.deposit(from: <-from.withdraw(amount: from.balane)) } } // if deposit failed for some reason if from.balance > 0.0 { emit NotCompletedDeposit(type: from.getType(), amount: from.balance, switchboardOwner: self.owner?.address) return <-from } destroy from return nil }
总之
Switchboard
资源作为一个第三方的路由资源, 给用户提供了一个通用的收款方式, 这样在之前 NFTMetadataViews
中关于 Royalty 的版税配置就可以接收多种 FT 资产了.从应用的角度来讲, 仍需要第三方开发者或钱包入口进行新合约的整合与引入, 能够方便用户自己手动配置和管理
Switchboard
中的资源.NFT borrowNFTSafe
另外 NFT 的标准合约也发布了更新, 在 NFT
CollectionPublic
的资源接口中新增了 borrowNFTSafe 函数, 目的是为了防止在查询脚本的执行过程中因无法找到账户下具体 ID 的 NFT 会导致脚本执行报错的情况,
提高了开发者的查询与开发的体验.最后
本次的更新还在测试网部署的阶段, 开发者可以在测试网体验新标准和新合约的功能, 待未来主网 spork 之后,这些新的代码将会部署, 届时 FT 就具备了链上查询 Metadata 的能力, 原有的 FT 发行方就可以根据示例的 FT 项目进行新函数的实现并升级自己的合约.
如此一来, 支持 FT 的 catalog 产品应该就不远了.