概览
Channel 转账是标准的 SPL 代币转账,提交至网关后无需专用的 Private Channels 转账指令。网关将交易路由至写入节点,由其在 Channel 的链下账户层上执行。转账在排序器处理时即告确认,而非等待 Solana 区块确认,且不会出现在主网上。
切换钱包 RPC
在发送转账之前,请将钱包的 RPC 端点切换为网关 URL,而非 Solana Devnet:
http://localhost:8899
发送至公共 Solana RPC 的交易将落在主网上,而非 Channel。
身份验证
如果您在启动堆栈时未使用 --profile auth(默认 devnet 配置),网关将在开放模式下运行,无需令牌——请跳过本节。
当 Auth Service 启用后(网关上已设置 JWT_SECRET),所有请求均需在 Authorization 标头中携带 JWT Bearer 令牌:
Authorization: Bearer <JWT_TOKEN>
请参阅 Authentication & Roles,了解如何注册、登录及获取令牌。
构建并发送转账
如果接收方的 associated token account 在 Channel 上尚不存在,请在转账指令之前添加 createAssociatedTokenAccountIdempotent 指令。以下代码同时处理两种情况:
import {address,createSolanaRpc,pipe,createTransactionMessage,setTransactionMessageFeePayerSigner,setTransactionMessageLifetimeUsingBlockhash,appendTransactionMessageInstruction,signAndSendTransactionMessageWithSigners,assertIsTransactionMessageWithSingleSendingSigner,getBase58Decoder} from "@solana/kit";import {findAssociatedTokenPda,getCreateAssociatedTokenIdempotentInstruction,getTransferInstruction} from "@solana-program/token";const TOKEN_PROGRAM_ADDRESS = address("TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA");// Point to the gateway, not a public Solana RPCconst privateChannelRpc = createSolanaRpc("http://localhost:8899");// Derive source and destination associated token accountsconst [sourceAta] = await findAssociatedTokenPda({mint: address(mintAddress),owner: address(senderAddress),tokenProgram: TOKEN_PROGRAM_ADDRESS});const [destinationAta] = await findAssociatedTokenPda({mint: address(mintAddress),owner: address(recipientAddress),tokenProgram: TOKEN_PROGRAM_ADDRESS});const transferInstruction = getTransferInstruction({source: sourceAta,destination: destinationAta,authority: transactionSigner,amount: 1_000_000n // 1 USDC (6 decimals)});// Create the destination ATA on the channel if it does not exist yetconst destinationAtaInfo = await privateChannelRpc.getAccountInfo(destinationAta, { encoding: "base64" }).send();const { value: latestBlockhash } = await privateChannelRpc.getLatestBlockhash({ commitment: "confirmed" }).send();// Sent to the Private Channels gateway (not Solana RPC directly), which// expects the legacy transaction format - do not switch this to version 0.const transactionMessage = !destinationAtaInfo.value? pipe(createTransactionMessage({ version: "legacy" }),(m) => setTransactionMessageFeePayerSigner(transactionSigner, m),(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),(m) =>appendTransactionMessageInstruction(getCreateAssociatedTokenIdempotentInstruction({payer: transactionSigner,ata: destinationAta,owner: address(recipientAddress),mint: address(mintAddress)}),m),(m) => appendTransactionMessageInstruction(transferInstruction, m)): pipe(createTransactionMessage({ version: "legacy" }),(m) => setTransactionMessageFeePayerSigner(transactionSigner, m),(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),(m) => appendTransactionMessageInstruction(transferInstruction, m));assertIsTransactionMessageWithSingleSendingSigner(transactionMessage);const signatureBytes =await signAndSendTransactionMessageWithSigners(transactionMessage);const signature = getBase58Decoder().decode(signatureBytes);console.log("Transfer signature:", signature);
验证转账
通过网关查询接收方的 Channel 余额,以确认转账已成功到账:
const [recipientAta] = await findAssociatedTokenPda({mint: address(mintAddress),owner: address(recipientAddress),tokenProgram: TOKEN_PROGRAM_ADDRESS});const balance = await privateChannelRpc.getTokenAccountBalance(recipientAta).send();console.log(balance.value.uiAmountString);
后续步骤
Withdraw Funds — 将代币提取回您的 Solana 钱包
Is this page helpful?