SPL Token
Solana 中的 ERC20 token 是一个 SPL(Solana Program Library) 中合约,实际上它并不是一个 Native Program, 但是由于它是由官方发布的高并发程序的标准,因此solana上的绝大多数代币统一以该程序发行。并且由于 solana 代码和数据分开的特性,协议的开发者不太可能去兼容其他的 token program,因为这要求 CPI 调用不可信的程序,这无法保证安全。
Token Program PubKey: TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA
同时新版的 Token-2022 正在beta中,预计2024年正式上线,目前主网上已经有两个不同的 token program id。
账户模型
SPL-Token 的主要account 是 spl_token::state::Mint, 每个 token 与一个 Mint Account 一一对应:
pub struct Mint {
/// Optional authority used to mint new tokens. The mint authority may only
/// be provided during mint creation. If no mint authority is present
/// then the mint has a fixed supply and no further tokens may be
/// minted.
pub mint_authority: COption<Pubkey>,
/// Total supply of tokens.
pub supply: u64,
/// Number of base 10 digits to the right of the decimal place.
pub decimals: u8,
/// Is `true` if this structure has been initialized
pub is_initialized: bool,
/// Optional authority to freeze token accounts.
pub freeze_authority: COption<Pubkey>,
}
Token的主要作用是记账,SPL-Token 采用一对多的账户模型,每个用户的 token 在独立的 account 中记账,类型为:spl_token::state::Account :
pub struct Account {
/// The mint associated with this account
pub mint: Pubkey,
/// The owner of this account.
pub owner: Pubkey,
/// The amount of tokens this account holds.
pub amount: u64,
/// If `delegate` is `Some` then `delegated_amount` represents
/// the amount authorized by the delegate
pub delegate: COption<Pubkey>,
/// The account's state
pub state: AccountState,
/// If is_native.is_some, this is a native token, and the value logs the
/// rent-exempt reserve. An Account is required to be rent-exempt, so
/// the value is used by the Processor to ensure that wrapped SOL
/// accounts do not drop below this threshold.
pub is_native: COption<u64>,
/// The amount delegated
pub delegated_amount: u64,
/// Optional authority to close the account.
pub close_authority: COption<Pubkey>,
}
每个用户都可以对同一个 token mint 生成多个 token account,并且这些token account是彼此独立的。
尽管一个 pubkey 可以和多个 token account 关联,但是为了 pubkey → pubkey 的 token transfer 过程可以约定一个唯一的 token account 地址,因此有一个和pubkey 一一对应的 token account 叫做 Associated Token Account。
Associated Token Account
Associated Token Account 由一个独立的program创建:ATokenGPvbdGVxr1b2hvZbsiqW5xWH25efTNsLJA8knL
其实就是一个生成 PDA,然后把 PDA owner assign 给 token program的过程:
PublicKey.findProgramAddressSync(
[
walletAddress.toBuffer(),
TOKEN_PROGRAM_ID.toBuffer(),
tokenMintAddress.toBuffer(),
],
SPL_ASSOCIATED_TOKEN_ACCOUNT_PROGRAM_ID
)[0]
注意:有些ix可能要求输入 ATA,但是实际上只检查了是不是 token account,这可能导致一些安全问题, ref:Security of Solana Smart Contracts: Two Caveats of the SPL Associated Token Account | Sec3 Blog
Wrap SOL
类似 WETH,有一个原生的 WSol token mint:So11111111111111111111111111111111111111112 ,called the "Native Mint"。
SPL-Token里为这个 mint pubkey做了很多特殊的分支,比如每个 token account 中 is_native flag。
向 is_native 的 token account 转账 SOL,就是Wrap Sol,需要注意的是,转入后需要手动调用 SPL-Token program 的 syncNative ix才能把转入的 sol 更新到 token account的余额中。
直接调用 token transfer 就会把对应的 sol 转移到dst地址。如果dst地址也是一个 wsol token account的话,也只是转移 sol,需要调用 syncNative 才能同步 wsol balance。
IX
initialize_mint
创建一个新的 token
pub fn initialize_mint(
token_program_id: &Pubkey,
mint_pubkey: &Pubkey,
mint_authority_pubkey: &Pubkey,
freeze_authority_pubkey: Option<&Pubkey>,
decimals: u8,
) -> Result<Instruction, ProgramError> {
这个 mint_pubkey 将作为一个新的 token 使用。
mint_to
只有 mint_authority_pubkey 可以mint token
approve
尽管user 直接调用 program时不再需要 approve(因为CPI中的signer传递),但是同样需要 approve接口因为可能需要在没有 signer 的时候也能让program转移代币。
这个ix实际上就是写入了如下两个值:
source_account.delegate = COption::Some(*delegate_info.key);
source_account.delegated_amount = amount;
注意,delegate和delegated_amount 并不是 maps,也就是说每个 token account只能 approve给一个 delegate 。
burn
除了 wrapped SOL ,其他 SPL-Token 都可以随便 burn。
CloseAccount
只有当 token account 的代币为0时,才能 close account,close后会退还rent sol。
FreezeAccount & ThawAccount
类似 USDC & USDT 的用户黑名单,SPL-Token 原生支持,需要 freeze_authority_pubkey 签名
syncNative
只能在 is_native 的token account上调用,会把account的lamports 写入 balance。
ref
Token Program | Solana Program Library Docs
Associated Token Account Program | Solana Program Library Docs
Token Program
Token-2022
Mainnet: TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb
source: https://github.com/solana-labs/solana-program-library/tree/master/token/program-2022