使用 Anchor 与 SPL Token Program 交互指南
使用Anchor与SPL Token Program交互指南
Anchor 是用来开发 Solana 智能合约的高层开发框架,它基于 Rust,简化了 Solana 原生开发的繁琐流程。
SPL 是 Solana Program Library
的缩写,它是Solana官方维护的一套标准智能合约程序集合,用来为 Solana 生态提供各种 基础功能模块,类似于以太坊里的 ERC标准合约。
创建Token Mint
首先要了解什么是Mint账户,简单来说,Mint 账户就是某个代币的“发行登记中心”,负责记录这个代币的总发行量、精度、小数位,以及谁有权限铸造(mint)或销毁(burn)它,所以它的结构就非常的清晰了,如下:
pub struct Mint { |
然后使用anchor-spl crate
中的token_interface
模块来与Token Program
和Token Extension Program
进行交互,以下是个简单的模板:
use anchor_spl::token_interface::{Mint, TokenInterface}; // 引入 Mint 账户类型和 Token 接口定义 |
**#[account(…)]**是一个 属性宏语法块, Anchor 框架扩展定义用来给紧随其后的账户字段添加 初始化规则或约束条件,在上面模板中就是一个账户的约束。
当然在创建mint账户时,也可以使用PDA作为你的 mint::authority
,那么可以让你的程序控制这个Token的铸造权限,而不是由某个外部用户来控制。创建地址如下:
|
创建Token账户
在 Solana 的 SPL Token标准中,每种代币(对应一个 Mint 账户)都有很多 Token账户,每个 Token账户属于一个特定的用户钱包地址,专门用来保存该用户持有的该种代币的数量。它的结构如下:
/// 账户数据结构。 |
此外,Token账户有个特定结构的Token账户——关联账户(ATA),就是某个用户的钱包地址(owner)+ 某个代币(mint)唯一对应的标准 Token 账户地址,它的用途是用于标准化存储某个钱包所持有的某种 SPL 代币的余额。
例如:钱包 A 拥有 100 个 USDC(mint = USDC 的地址) → 会有一个 ATA 专门存储这个 USDC 的余额
使用find_program_address(seed,bump)生成关联地址:
pub fn get_associated_token_address_and_bump_seed_internal( |
根据用户具体的使用,有俩种方式创建Token账户
当使用关联Token账户(ATA)时,使用associated_token约束.比如用户钱包
use anchor_spl::associated_token::AssociatedToken; // 导入 Anchor SPL 库中的关联 Token 程序相关功能
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; // 导入与 Token 程序相关的接口,Mint 是代币的铸造地址,TokenAccount 是代币账户,TokenInterface 是 Token 程序接口
// --snip--(省略的部分,通常是函数或程序主体)
// 声明 Anchor 账户结构体,标记为 Accounts,用于描述与程序交互时需要的账户。
pub struct CreateTokenAccount<'info> {
// 表示这个账户是可变的,将会在后续的操作中修改
pub signer: Signer<'info>, // signer 账户,表示谁发起这个操作,通常是一个钱包地址,用于支付创建账户的费用,并提供签名。
pub token_account: InterfaceAccount<'info, TokenAccount>, // 目标账户,即创建的 Token 账户,通过 InterfaceAccount 将账户与 TokenAccount 关联
pub mint: InterfaceAccount<'info, Mint>, // 代币的 mint 地址,表示 Token 的类型,用于标识该 Token 账户属于哪个代币种类
pub token_program: Interface<'info, TokenInterface>, // 指定与 Token 相关的程序,通常是 Solana 官方的 Token 程序,用于处理代币操作(如转账、查询等)
pub associated_token_program: Program<'info, AssociatedToken>, // 指定使用的关联 Token 程序,它负责管理与钱包地址关联的 Token 账户
pub system_program: Program<'info, System>, // Solana 的系统程序,用于账户和资源管理(例如账户创建)
}当使用非特定ATA的Token账户(如自定义PDA或使用密钥对公钥作为地址的Token账户)时,使用token约束,比如程序控制的账户
use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; // 导入 Anchor SPL 库中的 Token 相关接口,Mint 是代币的铸造地址,TokenAccount 是代币账户,TokenInterface 是 Token 程序接口
// --snip--(省略的部分,通常是函数或程序主体)
// 声明 Anchor 账户结构体,标记为 Accounts,用于描述与程序交互时需要的账户。
pub struct CreateTokenAccount<'info> {
// 表示这个账户是可变的,后续操作会修改它
pub signer: Signer<'info>, // signer 账户,表示操作发起者,通常是一个钱包地址,提供签名并支付交易费用
// 使用种子值 `token` 来派生一个程序派生地址(PDA) ,
bump // `bump` 是在计算派生地址时用于确保唯一性和合法性的附加参数
)]
pub token_account: InterfaceAccount<'info, TokenAccount>, // 创建的目标 Token 账户,表示该账户与 mint 地址关联
pub mint: InterfaceAccount<'info, Mint>, // 代币的 mint 地址,表示该 Token 账户所属的代币种类
pub token_program: Interface<'info, TokenInterface>, // 关联的 Token 程序,用于处理所有与 Token 相关的操作
pub system_program: Program<'info, System>, // Solana 系统程序,用于创建账户等基础操作
}
铸造代币
铸造代币是指通过调用 Token Program 上的 mint_to 指令创建新代币单位的过程。只有被指定为铸币账户上的铸币权限的地址才能铸造新代币。该指令还要求在代币接收地址上存在一个目标代币账户。
要通过 Anchor 程序铸造代币,你需要向 Token Program 或 Token Extension Program 的 指令发起跨程序调用(CPI)。
这意味着你要从程序中的指令调用 Token Program 或 Token Extension Program 上的 指令。你的程序作为中介,向代币程序传递所需的账户、指令数据和签名。
通过CPI铸造程序
MintTo结构体:
- mint- 用于创建新代币单位的铸币账户
- to- 用于接收新铸造代币的目标代币账户
- authority- 拥有铸造代币权限的铸币授权
use anchor_lang::prelude::*; // 引入 Anchor 框架的基础设施 |
解释CPI相关的部分:
- let cpi_accounts = MintTo { … };:构建一个 MintTo 结构体,表示代币铸造操作的账户信息.
- let cpi_program = ctx.accounts.token_program.to_account_info();:获取 Token 程序的账户信息,这是代币相关操作的程序。
- let cpi_context = CpiContext::new(cpi_program, cpi_accounts);:将 cpi_program 和 cpi_accounts 组合成一个 CPI 上下文,这将用来调用代币程序的 mint_to 函数。
- token_interface::mint_to(cpi_context, amount)?;:调用 SPL Token 程序的 mint_to 函数来实际铸造代币,传入 CPI 上下文和指定的数量 amount。
通过CPI使用PDA铸币授权铸造代币
使用 PDA 来授权铸币的目的是确保某些操作只能由程序控制,并且可以通过程序进行管理,而不是依赖于某个签名者的私钥。具体操作时,我们通常会使用特定的种子来派生 PDA,并将它设置为 mint::authority,这样就能通过这个 PDA 来授权铸币操作。
结合之前所讲的 Token Mint部分,就好理解了。
use anchor_lang::prelude::*; // 引入 Anchor 框架的标准库,提供基础功能 |
转移代币
转移代币涉及将代币从一个代币账户移动到另一个共享相同铸造的代币账户。这是通过在代币程序上调用 transfer_checked 指令来完成的。只有指定为源代币账户所有者(权限)的地址可以从该账户中转移代币。
通过CPI转移代币
使用token_interface::transfer_checked
函数对代币程序或代币扩展程序进行 CPI。此函数需要:
- TransferChecked结构体,该结构体指定所需的账户:
- mint- 指定要转移的代币类型的铸造账户
- from- 要转移代币的源代币账户
- to- 接收转移代币的目标代币账户
- authority- 源代币账户的所有者
- 要转移的代币 ,以代币的基本单位调整小数。(例如,如果铸造有 2 位小数,则 100 的数量 = 1 个代币)amount
use anchor_lang::prelude::*; // 导入 Anchor 的预定义模块 |
通过CPI使用PDA代币所有者转移代币
通过程序控制的 PDA 地址来充当代币账户的所有者,而程序通过 CPI 调用代币程序(Token Program)进行代币转移操作。
结合之前讲的Mint账户,铸造代币的内容,来实现通过CPI使用PDA代币所有者转移代币
use anchor_lang::prelude::*; // 引入 Anchor 的预定义模块,用于开发程序的基础功能。 |
以上就是一个创建Mint账户,Token账户,实现铸造代币,转移代币的过程。