在 Solana 上测试 token 逻辑,意味着在测试实际关心的转账逻辑之前,需要先设置铸币账户、token
account 和权限。来自 @solana-program/token 的 tokenProgram()
插件将这些设置压缩为几个方法调用——并自动处理 ATA 的创建。
安装
pnpm add @solana/kit @solana/kit-plugin-litesvm @solana/kit-plugin-signer @solana-program/token
创建铸币账户
import { createClient, generateKeyPairSigner, lamports } from "@solana/kit";import { litesvm } from "@solana/kit-plugin-litesvm";import { signer } from "@solana/kit-plugin-signer";import { tokenProgram } from "@solana-program/token";const mySigner = await generateKeyPairSigner();const client = createClient().use(signer(mySigner)).use(litesvm()).use(tokenProgram());client.svm.airdrop(client.payer.address, lamports(10_000_000_000n));const mintAuthority = await generateKeyPairSigner();const newMint = await generateKeyPairSigner();await client.token.instructions.createMint({newMint,decimals: 9,mintAuthority: mintAuthority.address}).sendTransaction();// Verifyconst mintAccount = await client.token.accounts.mint.fetch(newMint.address);console.log(mintAccount.data.supply); // 0nconsole.log(mintAccount.data.decimals); // 9
铸造 Token
mintToATA 将 token 发行至所有者的 associated token
account。如果该 ATA 不存在,则会在同一笔交易中自动创建。
await client.token.instructions.mintToATA({mint: newMint.address,owner: client.payer.address,mintAuthority,amount: 1_000_000_000_000n, // 1000 tokens (9 decimals)decimals: 9}).sendTransaction();
decimals 参数是一项安全检查——它必须与铸币账户的小数位数完全一致,否则 Token
Program 将以错误 0x24 拒绝该交易。这可防止意外铸造出多达 1000 倍数量的 token。
转账 Token
transferToATA 在用户之间转移 token。如有需要,接收方的 ATA 将自动创建。
const alice = await generateKeyPairSigner();const bob = await generateKeyPairSigner();// Mint to Aliceawait client.token.instructions.mintToATA({mint: newMint.address,owner: alice.address,mintAuthority,amount: 1_000_000_000_000n,decimals: 9}).sendTransaction();// Transfer 400 tokens from Alice to Bobawait client.token.instructions.transferToATA({mint: newMint.address,authority: alice,recipient: bob.address,amount: 400_000_000_000n,decimals: 9}).sendTransaction();
authority
是拥有源 token 的签名者。该插件根据其地址与铸币账户推导出源地址和目标地址的 ATA。
验证余额
要读取代币余额,请使用 findAssociatedTokenPda
推导 ATA 地址并获取已解码的账户:
import {findAssociatedTokenPda,TOKEN_PROGRAM_ADDRESS} from "@solana-program/token";const [aliceAta] = await findAssociatedTokenPda({owner: alice.address,mint: newMint.address,tokenProgram: TOKEN_PROGRAM_ADDRESS});const aliceAccount = await client.token.accounts.token.fetch(aliceAta);console.log(aliceAccount.data.amount); // 600_000_000_000nconsole.log(aliceAccount.data.mint); // newMint.addressconsole.log(aliceAccount.data.owner); // alice.address
精度位数
代币数量始终以最小单位表示。9 位小数的 1000 个代币应为
1_000_000_000_000n,而非 1000n。
// USDC has 6 decimalsconst oneUSDC = 10n ** 6n; // 1_000_000n// SOL has 9 decimalsconst oneSOL = 10n ** 9n; // 1_000_000_000n
代币数量请始终使用 BigInt。JavaScript 的 Number 在超过 2^53 时会丢失精度。
插件参考
| 方法 | 描述 |
|---|---|
client.token.instructions.createMint() | 创建新的 SPL token mint |
client.token.instructions.mintToATA() | 将代币铸造至所有者的 ATA(如需则自动创建 ATA) |
client.token.instructions.transferToATA() | 将代币转账至接收方的 ATA(如需则自动创建 ATA) |
client.token.accounts.mint.fetch() | 获取并解码 mint account |
client.token.accounts.token.fetch() | 获取并解码 token account |
常见错误
| 错误 | 原因 | 解决方法 |
|---|---|---|
custom program error: 0x24 | decimals 与 mint 不匹配 | 使用与传入 createMint 时相同的精度值 |
AccountNotFound | 账户尚不存在 | 请确保在铸造前已创建 mint |
InsufficientFunds | lamport 不足,无法支付 rent | 向付款方空投更多 SOL |
OwnerMismatch | 账户的所属程序不正确 | 检查程序 ID |
Is this page helpful?