Solana/泰铢币/进化之路

当我们在区块链世界中编写去中心化应用时, 往往都是从最简单的链上数据存储器起步. 大概在 8 年前, 我第一次接触到区块链世界, 我看到的第一个教程就是教学如何在以太坊上编写一个数据存储器. 如今, 我成为了一个新的教程编写者, 当我思考我应该选择哪个应用作为我的教学例子时, 我立即想到了它, 我必须承认, 这是一种开源精神的传承.

我很喜欢一句话: 算法 + 数据结构 = 程序. 我认为即使是去中心化应用也遵循这个道理. 当您理解如何在链上存储任意数据后, 您就能通过调整算法来实现任意您想实现的程序.

Algorithms + Data Structures = Programs 是 N. Wirth 老爷子的经典著作.

链上数据存储器的本质是用一个数据账户, 在链上存储用户自己的任意信息.

我们如果想把它发展成一个"泰铢币"程序, 只需要从数据格式, 指令交互, 账户管理上这三个方面做一些改变. 下面, 我们就从这些角度, 看看它是怎么从数据存储器一步步进化的.

账户模型: 从简单数据到余额账户

在最初的存储器中, 数据账户的结构很简单, 用户可以存储任意格式和长度的数据. 每个用户都有自己专属的数据账户, 合约只要校验 pda 地址和用户签名即可写入数据.

到了泰铢币程序, 我们就得让数据账户不仅仅是一个可以任意读写的个人空间, 而是真正的余额账户. 我们规定数据账户中只能存储一个 64 位无符号型整数, 且以大端序进行编码.

这样, 每个用户的数据账户就好像是在代币合约账本里的子账户, 明确记载了该用户拥有多少泰铢币.

两个指令: 铸造和转账

在链上数据存储器阶段, 程序只有一个存储或更新数据的指令. 现在我们需要基于这个指令, 开发出两个新的指令:

  1. 铸造: 由铸造权限持有者(通常是合约部署者)发起, 为所有者铸造新的泰铢币: 也就是货币增发.
  2. 转账: 用户 ada 转账泰铢币给用户 bob, 要求用户 ada 签名确认, 并更新双方的数据账户(余额账户).

这两个指令不仅要对余额账户读写, 还要进行基本的检查:

  1. 铸造: 只能由授权账户发起.
  2. 转账: 校验发送方余额是否足够, 并小心处理整数溢出问题.

设计两条指令的接收数据格式. 简单来说, 泰铢币程序只接收 9 个字节的数据, 第一个字节用于区分您是想铸造还是转账, 剩余的字节表示为铸造代币的数量或转账代币的数量.

  1. 铸造: 0x00 + u64
  2. 转账: 0x01 + u64

账户列表

每个指令都要明确声明它用到的账户(accounts 参数), 否则无法在 solana 运行. 需要额外注意的地方在于, 如果用户还不存在数据账户, 我们需要为他创建新的数据账户.

总结账户列表如下:

铸造

账户索引 地址 需要签名 可写 权限(0-3) 角色
0 ... 3 铸造权限所有者的普通钱包账户
1 ... 1 铸造权限所有者的数据账户
2 1111111111... 0 System
3 SysvarRent... 0 Sysvar rent

转账

账户索引 地址 需要签名 可写 权限(0-3) 角色
0 ... 3 发送者的普通钱包账户
1 ... 1 发送者的数据账户
2 ... 0 接收者的普通钱包账户
3 ... 1 接收者的数据账户
4 1111111111... 0 System
5 SysvarRent... 0 Sysvar rent

不是结束

跟以太坊的 erc20 不同, solana 的合约世界非常灵活. 我们需要管理铸造的权限. 在本教程中, 我们选择把铸造权限写死在合约里, 当然您也可以单独搞个"权限账户"来管理铸造权限.

您也可以随时添加别的功能, 比如销毁或者批量转账, 这些功能虽然不是很常用, 但对于某些场景至关重要, 例如您想批量空投代币到上百万个用户: 如果没有批量转账功能, 这花费的手续费以及时间很可能是您无法接受的.

从最初的链上数据存储器, 到一个真正的泰铢币程序, 关键在于:

  • 数据结构的演化. 从简单的字节串演进到余额账户结构.
  • 指令的演化. 从简单存储更新变成铸造和转账.
  • 账户列表的演化.

世界由您来定义, 见证您的泰铢币的诞生!