在 Solana 上,所有与代币相关的操作都由 SPL Token Program 和 Token2022 Program 处理:这是 Solana 的原生代币框架,定义了所有代币的创建、管理和转移方式。
这是一个单一的统一程序,处理整个网络中的所有代币操作,确保一致性和互操作性。
⚠️ 在 Solana 上为所有代币提供单一统一接口的决定,创造了一种可以在所有 dApp(去中心化应用程序)和集成(如钱包等)中复制的简单实现。
对于 Anchor,所有与代币相关的内容都可以在 anchor-spl crate 中找到。因此,在初始化了一个 Anchor 工作区后,我们可以这样做:
cargo add anchor-spl
⚠️ 如果您不熟悉 Anchor,我们建议先完成 Anchor 入门 再继续。
如果您熟悉 Anchor,您会知道它们有一组宏,可以帮助用户抽象掉与初始化账户相关的许多复杂性。
同样适用于 Mint、Token 和 Associated Token 账户。
得益于 Anchor 提供的宏,我们可以轻松创建一个 Mint 账户,如下所示:
#[derive(Accounts)]
pub struct CreateMint<'info> {
#[account(mut)]
pub signer: Signer<'info>,
#[account(
init,
payer = signer,
mint::decimals = 6,
mint::authority = signer.key(),
)]
pub mint: Account<'info, Mint>,
pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
}
同样适用于 Token 账户。通过宏创建 Token 账户的方式如下:
#[derive(Accounts)]
pub struct CreateToken<'info> {
#[account(mut)]
pub signer: Signer<'info>,
pub mint: Account<'info, Mint>,
#[account(
mut,
token::mint = mint,
token::authority = signer,
)]
pub token: Account<'info, TokenAccount>,
pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
}
同样适用于 Associated Token 账户,通过宏创建 Associated Token 账户的方式与创建 Token 账户类似,唯一的区别在于约束条件,具体如下:
#[derive(Accounts)]
pub struct CreateToken<'info> {
#[account(mut)]
pub signer: Signer<'info>,
pub mint: Account<'info, Mint>,
#[account(
mut,
associated_token::mint = mint,
associated_token::authority = signer,
)]
pub token: Account<'info, TokenAccount>,
pub system_program: Program<'info, System>,
pub token_program: Program<'info, Token>,
pub associated_token_program: Program<'info, AssociatedToken>,
}
创建新代币并将其存入指定账户。只有铸币权限持有者可以执行此操作。
在我们铸造任何代币之前,我们需要已经完成以下操作:
Mint 账户,并持有 mintAuthorityToken 账户或 Associated Token 账户,用于接收铸造的代币⚠️ 我们铸造的代币数量是根据小数位数进行“标准化”的。这意味着如果我们想铸造一个具有 6 位小数的代币,我们实际上需要将金额设置为
1_000_000
以下是 CPI 到 mint_to() 指令的示例:
mint_to(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
MintTo {
mint: ctx.accounts.mint_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
),
&1_000_000,
)?;
将代币从一个账户转移到另一个账户。这是用户之间发送代币的基本操作。
在我们可以转移任何代币之前,我们需要已经具备以下条件:
Mint 账户。Token 账户或 Associated Token 账户,并且该账户中至少有我们想要转移的数量。Token 账户或 Associated Token 账户,用于接收来自源 Token 账户的代币。⚠️ 我们转移的代币数量是根据小数位数进行“标准化”的。这意味着如果我们想要转移一个有 6 位小数的代币,我们实际上需要将
1_000_000作为数量。
以下是 CPI 到 transfer() 指令的样子:
transfer(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Transfer {
from: ctx.accounts.from_token_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
),
&1_000_000,
)?;
通过将代币从流通中移除来永久销毁代币。这将减少代币的总供应量。
在我们销毁任何代币之前,我们需要已经拥有:
Mint 账户。Token 账户或一个 Associated Token 账户,并且该账户中至少有我们想要销毁的数量。⚠️ 我们销毁的代币数量是根据小数位数进行“标准化”的。这意味着,如果我们想销毁一个有 6 位小数的代币,我们实际上需要将
1_000_000作为数量输入。
这是 CPI 到 burn() 指令的样子:
burn(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Burn {
mint: ctx.accounts.mint.to_account_info(),
from: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
),
&1_000_000,
)?;
关闭一个 token 账户,并将其剩余的 SOL 租金转移到目标账户。该 token 账户必须余额为零,除非它是一个原生 SOL 账户。
⚠️ 从 Token2022 开始,可以关闭供应量为 0 的
Mint账户。
在我们关闭任何 token 账户之前,我们需要已经完成以下操作:
Mint 账户Token 账户或一个没有任何 token 的 Associated Token 账户。以下是 CPI 到 close_account() 指令的示例:
close_account(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
CloseAccount {
account: ctx.accounts.token_account.to_account_info(),
destination: ctx.accounts.authority.to_account_info(),,
authority: ctx.accounts.authority.to_account_info(),
},
),
)?;
批准允许代理人代表账户所有者转移特定数量的代币。这使得程序化的代币转移成为可能,而无需授予完整的账户控制权。
⚠️ 我们设置一个“批准”的金额,代理人只能转移不超过该金额的代币
撤销会移除当前代理人对账户的权限,将完整的控制权还给账户所有者。
⚠️ 立即取消任何现有的代理权限,并且只有账户所有者可以撤销代理(代理人本身无法撤销)
在我们可以代理或撤销任何代币账户之前,我们需要已经完成以下操作:
Mint 账户。Token 账户或 Associated Token 账户,我们将对其进行控制⚠️ 我们铸造的代币数量是根据小数位数“标准化”的。这意味着如果我们想铸造一个有 6 位小数的代币,我们实际上需要将金额设置为
1_000_000
以下是 CPI 到 approve() 指令的样子:
approve(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Approve {
to: ctx.accounts.token_account.to_account_info(),
delegate: ctx.accounts.delegate.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
),
&1_000_000,
)?;
以下是 CPI 到 revoke() 指令的样子:
revoke(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
Revoke {
pub source: ctx.accounts.token_account.to_account_info(),
authority: ctx.accounts.authority.to_account_info(),
},
),
)?;
冻结会阻止账户上的所有代币操作,直到账户被解冻。只有代币的冻结权限持有者才能执行此操作。
⚠️ 完全禁用转账、授权和销毁操作,并且仅影响特定被冻结的账户
解冻会重新启用之前被冻结账户上的代币操作。只有代币的冻结权限持有者才能解冻账户。
⚠️ 恢复被冻结账户的全部功能,并且只能由代币的冻结权限持有者执行
在我们冻结或解冻任何代币账户之前,我们需要已经完成以下操作:
freezeAuthority 的 Mint 账户Token 账户或 Associated Token 账户以下是 CPI 到 freeze_account() 指令的示例:
freeze_account(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
FreezeAccount {
account: ctx.accounts.token_account.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),,
authority: ctx.accounts.authority.to_account_info(),
},
),
)?;
以下是 CPI 到 thaw_account() 指令的示例:
thaw_account(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
ThawAccount {
account: ctx.accounts.token_account.to_account_info(),
mint: ctx.accounts.mint.to_account_info(),,
authority: ctx.accounts.authority.to_account_info(),
},
),
)?;
更改 mint 或账户的权限。这允许转移所有权或更新特定的权限类型。
在我们设置任何代币或代币账户的权限之前,我们需要已经完成以下操作:
mintAuthority 或 freezeAuthority 的 Mint 账户Token 账户或 Associated Token 账户以下是 CPI 到 set_authority() 指令的示例:
set_authority(
CpiContext::new(
ctx.accounts.token_program.to_account_info(),
SetAuthority {
account_or_mint: ctx.accounts.mint_account.to_account_info(),
to: ctx.accounts.to_token_account.to_account_info(),
current_authority: ctx.accounts.authority.to_account_info(),
},
),
&spl_token::instruction::AuthorityType::MintTokens, // authority_type
Some(new_authority.key()) // new_autority
)?;
本节作者:blueshift