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 Program | Token-2022 |
|---|---|---|
| 生态兼容性 | 广泛支持 | 支持增长中,部分 DeFi 协议尚未完全支持 |
| 功能需求 | 基础转账、铸造、销毁 | 需要手续费、元数据扩展等高级功能 |
| Gas 成本 | 略低 | 略高(扩展需要更多计算) |
| 适用场景 | 通用代币 | 合规代币、收益代币、SBT |
建议:如果不需要 Token-2022 的特殊功能,优先使用原始 Token Program 以获得最广泛的兼容性。