Solana/在主网发行您的代币/由程序控制的代币

空投就像是给大家发糖果, 可是, 要实现一个自动发糖的合约(我们这里指的是空投合约), 首先必须确保合约有足够的糖果库存. 这就像是, 你想在派对上分发糖果, 首先得保证自己手里有糖果!

在 solana 上, 代币不会直接存储在钱包或程序里, 而是通过关联代币账户来管理. 由程序控制的代币也需要通过关联代币账户来管理和转移.

代币转账流程

我们再来复习一下关联代币账户. 代币的转账并不是直接发生在用户的钱包和接收账户之间, 而是通过关联代币账户来完成的. 你可以把它想象成你大钱包里的一个小专用钱包盒, 每个小钱包盒都对应一种代币.

代币转账是打开您的大钱包后, 再打开您的小钱包, 然后发送到另一个用户的小钱包里.

通常来讲, 要实现由程序控制的代币这个功能, 需要有以下三个账户层级.

  • 程序账户. 这儿的程序账户是就是我们的空投程序, 控制着代币的所有权. 它负责分配和转移代币. 程序账户通过执行 invoke 或 invoke_signed 指令来与其他账户交互.
  • 程序 pda 账户. 程序账户通常不能直接拥有代币, 程序账户可以通过派生一个 pda 账户来存储代币. 这个 pda 账户是通过程序地址和特定的种子生成的. 派生账户的签名权限是由程序账户通过 invoke_signed 来控制的.
  • 关联代币账户. 实际存储代余额的账户. 关联代币账户由程序的 pda 账户派生而来.

简单来说, 账户之间的关系: 程序账户 -> 程序 pda 账户 -> 关联代币账户.

真正拥有代币所有权的是程序 pda 账户, 但得以于 pda 账户的特性, 程序账户可以代替其 pda 账户签名, 因此程序账户也就变相拥有了代币的所有权.

工作原理详解

在 solana 中, 程序账户通过签名来控制 pda 账户, 使得程序能够代替 pda 账户进行代币管理. 这是由于 pda 账户本身没有私钥, 它的签名操作只能通过与程序账户相关的签名种子来完成.

假设你有一个程序, 它需要把代币转给用户. 那么过程如下.

  1. 程序账户通过 invoke_signed 指令向 pda 账户发起转账请求.
  2. pda 账户存储着代币, 程序通过签名种子签署转账, 完成从 pda 账户到用户关联代币账户的代币转移.

核心代码示例:

let account_seed = &[];
let account_bump = solana_program::pubkey::Pubkey::find_program_address(&[account_seed], account_mana.key).1;
solana_program::program::invoke_signed(
    &spl_token_2022::instruction::transfer_checked(
        &account_spl.key,
        &account_mana_spla.key,
        &account_mint.key,
        &account_user_spla.key,
        &account_mana_auth.key,
        &[],
        5000000000,
        9,
    )?,
    accounts,
    &[&[account_seed, &[account_bump]]],
)?;