本文深入探讨Solana的SPL Token Mint,详细介绍了其常用指令和数据结构,包括如何初始化mint、token账户,铸造、燃烧和转移代币等。通过逐步分析每个指令的实现和逻辑,读者能够全面理解SPL token的工作原理和应用场景。
Solana 代币程序( 源代码)是最常执行的 Solana 智能合约之一。其程序 ID: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
在本文中,我们详述 SPL 代币并介绍在代币程序中最常用的指令的内部实现:
指令 | SPL 功能 |
---|---|
InitializeMint | spl_token::instruction::initialize_mint |
InitializeAccount | spl_token::instruction::initialize_account |
MintTo | spl_token::instruction::mint_to |
Burn | spl_token::instruction::burn |
Transfer | spl_token::instruction::transfer |
SyncNative | spl_token::instruction::sync_native |
Approve | spl_token::instruction::approve |
Revoke | spl_token::instruction::revoke |
FreezeAccount | spl_token::instruction::freeze_account |
ThawAccount | spl_token::instruction::thaw_account |
CloseAccount | spl_token::instruction::close_account |
SetAuthority | spl_token::instruction::set_authority |
这些指令的实现包含三个重要的数据结构:
InitializeMint 指令是 SPL 代币铸造过程中第一个调用的指令。它将调用函数 _process_initialize_mint 来初始化铸造账户(在第 36 行中,accounts 向量中的第一个账户提供的为 mint_info):
铸造账户的数据被反序列化为 mint 的 Mint 数据类型,然后检查重新初始化错误:
Mint 结构有五个属性:
最后,通过设置其 mint_authority、decimals、将 is_initialized 标志设置为 true,以及一个可选的 freeze_authority 来初始化铸造账户。
注意,在上述初始化代码中,铸造的 supply 没有设置,但默认为 0。
该指令将调用函数 _process_initialize_account 来初始化一个代币账户(在第 90 行,new_account_info 提供为 accounts 向量中的第一个账户):
与 InitializeMint 类似,代币账户的数据被反序列化为 account 的 Token 数据类型,并检查重新初始化错误:
Token 结构具有八个属性:
最后,通过设置其 mint、owner、close_authority(默认为 None)、delegate(默认为 None)、delegated_amount(默认为 0)、state(已初始化)、is_native 标志和 amount(即该账户所持有的代币数量)来初始化代币账户:
注意,当代币铸造为本地铸造时,即 WSOL(包装的 SOL,程序 ID: So11111111111111111111111111111111111111112),is_native 为 true,amount 被初始化为代币账户中的 lamports 数量(减去租金)。
在铸造和代币账户被初始化后,可以通过调用 MintTo 指令来铸造代币,触发函数 process_mint_to:
MintTo 指令接受三个用户提供的账户:
它还接受两个用户提供的指令数据:
注意,对于 MintTo 指令有几个有效性检查:
最后,如果处理 MintTo 指令成功,目标账户的代币金额( destination_account.amount)和铸造的供应量( mint.supply)将都增加 amount :
Burn 指令(大多数情况下)与 MintTo 相对。它调用函数 process_burn 从源代币账户销毁一定数量的代币:
如果 Burn 指令成功,源账户的代币数量( source_account.amount)和铸造的供应量( mint.supply)都将减少 amount :
一个主要的区别是,签名的权限账户不是铸造的权限,而是源账户的所有者或委托者。对于后者,逻辑稍显复杂:
Transfer 指令将调用函数 process_transfer 将一定数量的代币从源账户转移到目标账户:
该函数有几个重要的有效性检查:
注意: source_account 可能与 destination_account 相同。如果是这样,则为自我转移,函数将简单地返回 Ok。换句话说,自我转移是代币程序允许的 。
最后,如果 Transfer 指令成功,source_account 的代币数量减少 amount,而 destination_account 的代币数量增加 amount :
注意: 对于原生代币账户(即,代币铸造为 WSOL),为了能够通过代币程序直接转移 SOL,代币金额应“同步”到账户中 lamports 的数量。
因此,source_account 和 destination_account 的 lamports 也必须相应更新:
对于原生代币账户,当代币金额小于账户中的 lamports 数量(可能由于将 lamports system_instruction 转移到代币账户造成),代币程序有 SyncNative 指令来同步这两个值:
在上面的代码中,本地账户的数量更新为账户中 lamports 的数量减去租金( 第 768 行)。
Approve 指令将调用函数 process_approve 为源账户设置一个委托账户及其委托金额,权限由源账户的权限提供:
注意,代币账户一次只允许一个委托。
Approve 指令将覆盖之前委托的设置和上一个 Approve 指令设置的委托金额。即使之前的委托金额没有被花费或只部分被花费,它仍将被覆盖。
Revoke 指令将调用函数 process_revoke 将源账户的委托设置为 None,并将委托金额设置为 0:
FreezeAccount 和 ThawAccount 指令将调用函数 process_toggle_freeze_account 来分别将源账户的状态设置为 Frozen 和 Initialized:
注意,该指令必须由铸造账户的 freeze_authority 签名(第 716 行 mint_info)。如果铸造的 free_authority 未设置,则将失败。
CloseAccount 指令将调用函数 process_close_account 来关闭一个代币账户( source_account),并通过源账户的权限将其所有 lamports 转移到另一个代币账户( destination_account):
关闭代币账户包括三个步骤:
注意,源账户必须与目标账户不同:
SetAuthority 指令用于为铸造或代币账户设置新的权限。权限可以是单个账户(钱包或 PDA)或多重签名账户(稍后将详细说明)。
要设置的权限类型由 authority_type 指定。共有四种不同类型:
Multisig 账户有 Multisig 数据类型及四个属性:
Multisig 账户可以通过 InitializeMultisig 指令初始化(第 175 行中的 multisig_info 账户):
在初始化时,m 作为参数传入,n 为有效签名账户的长度(第 192 行,signer_infos 提供为 accounts 向量):
我们将继续在下一些文章中介绍其他流行的 Solana 程序的技术细节(例如,token-swap、stake-pool、associated-token-account)。
sec3 是一家安全研究公司,致力于为数百万用户准备 Solana 项目。sec3 的 Launch Audit 是一项严格的、由研究人员主导的代码检查,调查并认证主网级别的智能合约;sec3 的持续审计软件平台 X-ray 与 GitHub 集成,渐进式扫描拉取请求,帮助项目在部署前加强代码;sec3 的部署后安全解决方案 WatchTower 确保资金安全。sec3 正在为 Web3 项目构建基于技术的可扩展解决方案,以确保协议在规模扩大时保持安全。
要了解更多关于 sec3 的信息,请访问 https://www.sec3.dev
- 原文链接: sec3.dev/blog/solana-pro...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!