6. Token Extensions (Token-2022)

6. Token Extensions (Token-2022)

Token Extensions(也称 Token-2022)是 SPL Token 的升级版本,提供了更多高级功能。它是一个独立的程序,与原始 Token Program 并行存在。

6.1 概述

Token-2022 程序地址:TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb

主要扩展功能:

扩展功能应用场景
Transfer Fee每笔转账自动收取手续费协议收入、通缩机制
Interest Bearing代币余额自动计息储蓄产品、收益代币
Non-Transferable禁止转让的灵魂绑定代币身份凭证、成就徽章
Permanent Delegate永久委托权限合规代币、可撤销资产
Transfer Hook转账时触发自定义逻辑版税、合规检查
Metadata链上存储代币元数据无需 Metaplex 的轻量方案
Confidential Transfer隐藏交易金额隐私交易

6.2 创建带扩展的代币

以下示例展示如何创建带有转账手续费的代币:

tsx
// create-token-with-fee.ts import { ExtensionType, createInitializeMintInstruction, createInitializeTransferFeeConfigInstruction, getMintLen, TOKEN_2022_PROGRAM_ID, } from '@solana/spl-token'; import { Connection, Keypair, SystemProgram, Transaction, sendAndConfirmTransaction, } from '@solana/web3.js'; import { connection, loadWallet } from './config'; async function createTokenWithTransferFee() { const payer = loadWallet(); const mintKeypair = Keypair.generate(); // 配置参数 const decimals = 6; const feeBasisPoints = 100; // 1% 手续费 (100 / 10000) const maxFee = BigInt(1_000_000); // 最大手续费:1 个代币 // 计算 Mint 账户所需空间(包含扩展) const extensions = [ExtensionType.TransferFeeConfig]; const mintLen = getMintLen(extensions); // 计算租金 const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); console.log('创建带转账手续费的代币...'); console.log('Mint 账户大小:', mintLen, 'bytes'); console.log('手续费率:', feeBasisPoints / 100, '%'); // 构建交易 const transaction = new Transaction().add( // 创建账户 SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mintKeypair.publicKey, space: mintLen, lamports, programId: TOKEN_2022_PROGRAM_ID, }), // 初始化转账手续费配置 createInitializeTransferFeeConfigInstruction( mintKeypair.publicKey, payer.publicKey, // 手续费收取权限 payer.publicKey, // 手续费提取权限 feeBasisPoints, maxFee, TOKEN_2022_PROGRAM_ID ), // 初始化 Mint createInitializeMintInstruction( mintKeypair.publicKey, decimals, payer.publicKey, // Mint Authority null, // Freeze Authority TOKEN_2022_PROGRAM_ID ) ); // 发送交易 const signature = await sendAndConfirmTransaction( connection, transaction, [payer, mintKeypair] ); console.log('创建成功!'); console.log('Mint 地址:', mintKeypair.publicKey.toBase58()); console.log('交易签名:', signature); return mintKeypair.publicKey; } createTokenWithTransferFee();

6.3 使用 Token-2022 进行转账

使用 Token-2022 代币时,需要显式指定程序 ID:

tsx
// transfer-token-2022.ts import { getOrCreateAssociatedTokenAccount, transfer, TOKEN_2022_PROGRAM_ID, getTransferFeeAmount, getMint } from '@solana/spl-token'; import { PublicKey } from '@solana/web3.js'; import { connection, loadWallet } from './config'; async function transferWithFee( mintAddress: string, recipientWallet: string, amount: number ) { const payer = loadWallet(); const mint = new PublicKey(mintAddress); const recipient = new PublicKey(recipientWallet); // 获取 Mint 信息(使用 Token-2022 程序) const mintInfo = await getMint( connection, mint, 'confirmed', TOKEN_2022_PROGRAM_ID ); const rawAmount = BigInt(amount * (10 ** mintInfo.decimals)); // 创建 Token Accounts(指定 Token-2022 程序) const sourceAccount = await getOrCreateAssociatedTokenAccount( connection, payer, mint, payer.publicKey, false, 'confirmed', undefined, TOKEN_2022_PROGRAM_ID ); const destinationAccount = await getOrCreateAssociatedTokenAccount( connection, payer, mint, recipient, false, 'confirmed', undefined, TOKEN_2022_PROGRAM_ID ); // 计算转账手续费 const fee = getTransferFeeAmount( mintInfo, rawAmount, 'newerTransferFee' ); console.log('转账金额:', amount); console.log('预计手续费:', fee?.fee.toString() || '0'); console.log('接收方实收:', (rawAmount - (fee?.fee || 0n)).toString()); // 执行转账 const signature = await transfer( connection, payer, sourceAccount.address, destinationAccount.address, payer, rawAmount, undefined, undefined, TOKEN_2022_PROGRAM_ID ); console.log('转账成功!'); console.log('交易签名:', signature); }

6.4 元数据扩展

Token-2022 允许将元数据直接存储在 Mint 账户中,无需依赖外部程序:

tsx
// create-token-with-metadata.ts import { ExtensionType, createInitializeMintInstruction, createInitializeMetadataPointerInstruction, getMintLen, TOKEN_2022_PROGRAM_ID, TYPE_SIZE, LENGTH_SIZE, } from '@solana/spl-token'; import { createInitializeInstruction, createUpdateFieldInstruction, pack, TokenMetadata, } from '@solana/spl-token-metadata'; import { Keypair, SystemProgram, Transaction, sendAndConfirmTransaction, } from '@solana/web3.js'; import { connection, loadWallet } from './config'; async function createTokenWithMetadata() { const payer = loadWallet(); const mintKeypair = Keypair.generate(); // 定义元数据 const metadata: TokenMetadata = { mint: mintKeypair.publicKey, name: 'Solana University Token', symbol: 'SOLU', uri: 'https://example.com/token-metadata.json', additionalMetadata: [ ['description', 'Solana 大学学习代币'], ['website', 'https://academy.soldevcamp.com'] ], }; // 计算元数据大小 const metadataExtension = TYPE_SIZE + LENGTH_SIZE; const metadataLen = pack(metadata).length; // 计算 Mint 账户所需空间 const extensions = [ExtensionType.MetadataPointer]; const mintLen = getMintLen(extensions) + metadataExtension + metadataLen; const lamports = await connection.getMinimumBalanceForRentExemption(mintLen); console.log('创建带元数据的代币...'); console.log('代币名称:', metadata.name); console.log('代币符号:', metadata.symbol); const transaction = new Transaction().add( // 创建账户 SystemProgram.createAccount({ fromPubkey: payer.publicKey, newAccountPubkey: mintKeypair.publicKey, space: mintLen, lamports, programId: TOKEN_2022_PROGRAM_ID, }), // 初始化元数据指针 createInitializeMetadataPointerInstruction( mintKeypair.publicKey, payer.publicKey, mintKeypair.publicKey, // 元数据存储在 Mint 账户自身 TOKEN_2022_PROGRAM_ID ), // 初始化 Mint createInitializeMintInstruction( mintKeypair.publicKey, 6, payer.publicKey, null, TOKEN_2022_PROGRAM_ID ), // 初始化元数据 createInitializeInstruction({ programId: TOKEN_2022_PROGRAM_ID, mint: mintKeypair.publicKey, metadata: mintKeypair.publicKey, name: metadata.name, symbol: metadata.symbol, uri: metadata.uri, mintAuthority: payer.publicKey, updateAuthority: payer.publicKey, }) ); // 添加自定义字段 for (const [key, value] of metadata.additionalMetadata) { transaction.add( createUpdateFieldInstruction({ programId: TOKEN_2022_PROGRAM_ID, metadata: mintKeypair.publicKey, updateAuthority: payer.publicKey, field: key, value: value, }) ); } const signature = await sendAndConfirmTransaction( connection, transaction, [payer, mintKeypair] ); console.log('创建成功!'); console.log('Mint 地址:', mintKeypair.publicKey.toBase58()); console.log('交易签名:', signature); } createTokenWithMetadata();

6.5 Token Program vs Token-2022 如何选择

考虑因素Token ProgramToken-2022
生态兼容性广泛支持支持增长中,部分 DeFi 协议尚未完全支持
功能需求基础转账、铸造、销毁需要手续费、元数据扩展等高级功能
Gas 成本略低略高(扩展需要更多计算)
适用场景通用代币合规代币、收益代币、SBT

建议:如果不需要 Token-2022 的特殊功能,优先使用原始 Token Program 以获得最广泛的兼容性。

Playground
EDITOR ACTIVE
Initializing TS Environment...

Token-2022 扩展实验室

Mint Account

Token Configuration

Token-2022 Program
No extensions active. Behaves like standard SPL Token.
Sender
5000
Receiver
0