在EVM和TON Chain上开发NFT的技术差异
对于DApp开发者来说,发行FT或NFT通常是基本需求。因此,我选择NFT作为入门学习的起点。首先,让我们比较在EVM技术栈和TON Chain上开发NFT的区别。
在EVM中,NFT通常采用ERC-721标准。ERC-721定义了不可分割的加密资产,每个资产都是唯一的,并且可以附带元数据,如图片链接或其他属性。下面是一个常见的ERC-721接口示例。与FT不同,转账接口需要指定tokenID而不是数量,这保证了每个NFT资产的唯一性。为了支持更多属性,通常会为每个tokenID记录metadata,这是一个外部链接,存储NFT的可扩展数据。
在Solidity或面向对象编程中,开发这样的智能合约相对容易。只需定义合约需要的数据类型,如一些关键的映射关系(mapping),并实现修改数据的逻辑即可。
然而,在TON Chain上情况有所不同,主要原因有两点:
首先,TON中的数据存储是基于Cell实现的,同一账户的Cell通过有向无环图来管理。这种设计意味着无限增长的数据可能导致高昂的查询成本和合约死锁问题。
其次,为了提升并发性能,TON放弃了串行执行架构,采用了Actor模型,支持并行执行。这导致智能合约之间只能通过异步内部消息进行调用,无论是状态修改还是只读操作,都需遵循这一原则。开发者还需考虑处理数据回滚的方式,以应对异步调用可能带来的执行失败。
虽然在上一篇文章中已经详细讨论了这些技术差异,本文希望聚焦于智能合约开发,因此不再详细展开其他技术差异。总之,以上两点设计原则使得TON智能合约开发与EVM有着显著区别。
在明确了架构后,接下来需要解决核心功能需求。在TON中,NFT合约需要设计映射关系(mapping),如owners,用于存储每个tokenID对应的NFT所有者地址,决定NFT的所有权。为了避免无限增长的数据结构,官方建议采用主从合约模式,通过创建子合约管理每个key对应的数据,并通过主合约管理全局参数,如NFT名称、符号、总供应量等。
设计完架构后,接下来要考虑如何处理核心功能需求。由于使用了主从合约模式,需要明确主合约和子合约承载的功能,并设计内部消息交互流程。此外,也需仔细考虑内部调用失败后的数据回滚逻辑。即使是简单的NFT开发,也需要进行类似的验证。
从源码学习TON智能合约开发
TON选择了一种静态类型语言Func作为智能合约开发语言,类似于C语言。下面让我们从源码学习如何开发TON智能合约。我选择了TON官方文档中的NFT示例来进行讲解,有兴趣的读者可以查阅。本例中,我将展示如何实现一个简单的TON NFT合约。首先,让我们看看合约结构,共包括两个主要功能合约和三个必要的库。
这两个主要功能合约遵循上述原则进行设计。首先,让我们来看看主合约nft-collection的代码:
这里首先介绍了如何在TON智能合约中实现数据持久化存储。我们知道,在Solidity中,EVM会根据数据类型自动处理智能合约的状态变量,通常情况下,智能合约的状态变量将在执行结束后根据最新值自动持久化存储。但在Func中,开发者需要自己实现这些逻辑。这类似于C或C++需要开发者考虑GC过程,而其他新开发语言通常会自动处理这部分逻辑。接下来,让我们来看看代码。首先导入必要的库,然后第一个函数load_data用于读取持久化存储的数据。它首先通过get_data返回持久化合约存储的cell,这是通过stdlib.fc标准库实现的。一些函数可以视为系统函数来使用。
load_data函数返回cell类型,这是TVM中的cell类型。我们已经知道,TON区块链中所有的持久化数据都存储在cell树中。每个cell最多有1023位任意数据和最多四个对其他cell的引用。cell在基于堆栈的TVM中用作内存。cell中存储的是经过紧编码后的数据,要想获得其中具体的明文数据,需要将cell转换为slice类型。cell可以通过begin_parse函数转换为slice类型,然后可以从slice加载数据位和对其他cell的引用来获取cell中的数据。注意,代码第15行的调用方式是func中的一种语法糖,可以直接调用第一个函数返回值的第二个函数。最后,按数据持久化顺序加载相应的数据。注意,这与Solidity中不同,不是基于hashmap调用的。因此,调用顺序不可乱。
在save_data函数中,逻辑类似,但这是一个反向过程。这引入了下一个知识点,一个新的类型builder,这是cell构建器的类型。数据位和对其他cell的引用可以存储在builder中,然后builder可以最终化为新cell。首先通过标准函数begin_cell创建一个builder,然后通过store相关函数存储相关函数。注意,前文中调用顺序与此处存储顺序需要保持一致。最后,通过最外层的set_data,完成对该cell的持久化存储。
接下来让我们看看业务相关函数,首先需要介绍如何通过合约创建新的合约。我们知道,在TON中,智能合约之间的调用是通过发送内部消息的方式实现的。这是通过一个名为send_raw_message的函数来实现的。注意,第一个参数是message编码后的cell,第二个参数是标识位,用于指明该