安装
请确保您已安装所有必要的依赖项:
cargo add --dev litesvm litesvm-token solana-sdk spl-token spl-associated-token-account
SPL 代币基础
在 Solana 中,创建 token account 是一个两步流程。
创建 Mint Account
- 没有代币余额
- 保存代币的所有全局信息,如总供应量、小数位数、权限等
- 每种代币对应一个 mint account
- 所有者为 Token Program(TokenKeg 或 Token 2022)
创建 Token Account
- 存储特定 SPL 代币的余额
- 持有 mint account 以定义该账户中的 SPL 代币类型
- 一个 mint account 可以对应多个 token account
- 所有者即对该账户内代币拥有控制权的人
Token Account 类型
普通 Token Account
Token account 存储您特定 SPL 代币的余额:
// Can create token accounts at ANY addresslet token_account = Keypair::new(); // Random addresslet create_ix = system_instruction::create_account(&payer.pubkey(),&token_account.pubkey(), // Any address you wantrent,165, // Token account size&spl_token::id(),);let init_ix = spl_token::instruction::initialize_account(&spl_token::id(),&token_account.pubkey(),&mint,&owner.pubkey(),)?;
✓ 优点
- •可为相同的 mint/所有者创建多个账户
- •灵活——可使用任意地址
✗ 缺点
- •非确定性 - 需要手动追踪地址
- •收款方必须告知您要发送至哪个账户
- •多个账户容易造成混乱
适用场景: 临时/托管账户、具有自定义逻辑的程序所有账户、需要为同一代币创建多个账户时,以及高级 DeFi 策略。
Associated Token Account (ATA)
ATA 是位于确定性 PDA 地址的 token account:
// ATA address is ALWAYS the same for owner + mintlet ata = get_associated_token_address(&owner.pubkey(), &mint);// Address derived from: [owner_pubkey, token_program_id, mint]
ATA 地址的派生方式:
// ATA is a PDA owned by the Associated Token Programlet (ata, bump) = Pubkey::find_program_address(&[owner.as_ref(),spl_token::id().as_ref(),mint.as_ref(),],&spl_associated_token_account::id(), // ATA program);
✓ 优点
- •每个所有者/铸币对拥有唯一的标准账户
- •确定性 - 任何人均可计算出该地址
- •简化支付流程 - 只需钱包地址和铸币地址即可
- •所有 Solana 应用通用的标准规范
✗ 缺点
- •每个所有者/铸币对只能拥有一个 ATA(设计如此)
适用于大多数场景: 钱包应用、DeFi 协议、NFT 持仓、支付系统,以及任何面向用户的代币转账。
快速示例
以下是创建代币铸造账户并铸造代币的完整示例:
use litesvm::LiteSVM;use litesvm_token::{get_spl_account,spl_token::{native_mint::DECIMALS, state::Account as TokenAccount},CreateAccount, CreateMint, MintTo, Transfer,};use solana_sdk::{native_token::LAMPORTS_PER_SOL,signature::{Keypair, Signer},};#[test]fn test_create_and_mint_tokens() {let mut svm = LiteSVM::new();// Create payer account and fund itlet payer = Keypair::new();svm.airdrop(&payer.pubkey(), 10 * LAMPORTS_PER_SOL).unwrap();// Create a new SPL token mint with the payer as the mint authoritylet mint = CreateMint::new(&mut svm, &payer).authority(&payer.pubkey()).decimals(DECIMALS).send().unwrap();// Create a token account for the payerlet token_account = CreateAccount::new(&mut svm, &payer, &mint).owner(&payer.pubkey()).send().unwrap();// Mint tokens into the payer's token accountMintTo::new(&mut svm, &payer, &mint, &token_account, 1000).owner(&payer).send().unwrap();// Verify balancelet token_account: TokenAccount = get_spl_account(&svm, &token_account).unwrap();let account_balance = token_account.amount;assert_eq!(account_balance, 1000)}
核心概念
代币精度(Decimals)
大多数代币使用小数位来表示分数数量:
// SOL has 9 decimalslet one_sol = 10_u64.pow(9); // 1_000_000_000 lamports// USDC has 6 decimalslet one_usdc = 10_u64.pow(6); // 1_000_000 micro-USDC// Always account for decimals in calculationslet amount_tokens = 100;let amount_raw = amount_tokens * 10_u64.pow(decimals as u32);
账户关系
理解账户之间的关系至关重要:
- 铸造账户(Mint Account):定义代币(供应量、精度、权限)
- token account:为特定所有者持有代币
- associated token account:为所有者与铸造账户组合生成的确定性 token account
- 铸造权限(Mint Authority):可创建新代币
- 冻结权限(Freeze Authority):可冻结 token account(可选)
故障排查
常见错误
| 错误 | 原因 | 解决方案 |
|---|---|---|
AccountNotFound | 尝试使用一个不存在的账户 | 确保账户在使用前已创建 |
AccountAlreadyInitialized | 尝试初始化一个已初始化的账户 | 在创建前检查账户是否已存在 |
InsufficientFunds | lamport 不足以支付 rent 或代币不足以完成转账 | 确保充足的资金或铸造量 |
OwnerMismatch | 账户归属于错误的程序 | 创建时验证正确的程序 ID |
Is this page helpful?