在本地构建并在 devnet 上测试是开始使用 Solana 支付的好方法。然而,当你准备部署到主网时,需要注意主网的一些细节。devnet 可以容忍错误,而主网则不行。本指南将介绍关键差异,帮助你的用户获得流畅的体验。
| Devnet | Mainnet |
|---|---|
| 可以通过水龙头免费获取 SOL | 获取真实 SOL 用于支付手续费 |
| 区块空间竞争小 | 优先费很重要 |
| 交易容易确认 | 交易配置至关重要 |
| 公共 RPC 可用 | 生产环境需使用专用 RPC |
| Devnet 密钥对和代币铸造 | 主网需使用不同的密钥和代币铸造——请更新你的配置 |
RPC 基础设施
公共端点
(api.mainnet-beta.solana.com)有速率限制且无 SLA。它们适合开发,但在生产支付流程中会失败——就像试图通过没有可用性保障的共享 API 运行支付处理器一样。
切勿在生产环境中使用公共 RPC
请使用 专用 RPC 服务商 以获得可靠、低延迟的访问。
选择 RPC 服务商时,请关注:
- 可靠性:具备高可用性 SLA(99.9% 及以上)
- 延迟:靠近用户的地理位置
- 功能:交易落地、索引、优先费 API 等功能
完整的 RPC 服务商列表请参见 RPC 基础设施服务商 指南。
冗余 RPC 配置
和其他网络服务商一样,RPC 服务商也可能出现宕机或性能下降。为了确保你的应用具备弹性,建议配置多个 RPC 服务商。
Solana Kit 提供了一个用于自定义 RPC 传输的库,可以让你构建自己的冗余 RPC 客户端。以下是一个使用该库构建冗余 RPC 客户端的示例:
import { RpcTransport } from "@solana/rpc-spec";import { RpcResponse } from "@solana/rpc-spec-types";import { createHttpTransport } from "@solana/rpc-transport-http";// Create a transport for each RPC serverconst transports = [createHttpTransport({ url: "https://mainnet-beta.my-server-1.com" }),createHttpTransport({ url: "https://mainnet-beta.my-server-2.com" }),createHttpTransport({ url: "https://mainnet-beta.my-server-3.com" })];// Create a wrapper transport that distributes requests to themlet nextTransport = 0;async function roundRobinTransport<TResponse>(...args: Parameters<RpcTransport>): Promise<RpcResponse<TResponse>> {const transport = transports[nextTransport];nextTransport = (nextTransport + 1) % transports.length;return await transport(...args);}
如果你不想自己构建路由工具,可以利用第三方服务,例如 Iron Forge,来为你处理路由。
交易落地
在 Devnet 上,交易通常可以较容易地落地。而在 Mainnet 上,你需要与他人竞争区块空间。为了提高你的交易被包含进区块的概率,你应确保正确组装交易,包括:
- 在发送交易前包含一个新的 blockhash
- 在交易中加入带有有竞争力优先费用的优先费用指令
- 在交易中加入基于预估计算单元需求的计算单元限制指令
此外,你还可以考虑使用 Jito Bundles 等其他工具,以进一步提升交易被包含进区块的概率。我们接下来将更详细地介绍这些工具。
交易发送配置
在 Mainnet 上发送交易时,配置以下参数以获得最佳落地率:
Blockhash 管理:
- 使用
confirmedcommitment 获取 - 存储
lastValidBlockHeight,该值由getLatestBlockhash返回——它会告诉你交易何时过期 - blockhash 大约在 150 个区块后(约 60-90 秒)过期
发送选项:
maxRetries: 0— 禁用自动 RPC 重试。请自行处理重试,以便在需要时刷新 blockhash。skipPreflight: true— 发送前跳过模拟。当你已验证交易并希望获得最低延迟时使用。在开发阶段,保持false以便及早发现错误。
import { createSolanaRpc } from "@solana/kit";const rpc = createSolanaRpc(process.env.RPC_URL!);// 1. Get blockhash with confirmed commitmentconst { value: latestBlockhash } = await rpc.getLatestBlockhash({ commitment: "confirmed" }).send();// 2. Build and sign your transaction with the blockhash// ... (transaction building code)// 3. Send with production settingsconst signature = await rpc.sendTransaction(encodedTransaction, {encoding: "base64",maxRetries: 0n, // Handle retries yourselfskipPreflight: true, // Skip simulation for speed (use false during dev)preflightCommitment: "confirmed"}).send();// 4. Track expiration using lastValidBlockHeightconst { lastValidBlockHeight } = latestBlockhash;// Stop retrying when current block height exceeds lastValidBlockHeight
使用优先费用
每笔 Solana 交易都需要支付交易手续费,费用以 SOL 结算。交易手续费分为两部分:基础费用和优先费用。基础费用用于补偿验证者处理交易。优先费用是可选的,支付后可以提高当前 leader 处理你交易的概率。你可以把它理解为“加急快递”:支付更多费用,获得更快、更可靠的处理。
费用运作方式:
Total fee = Base fee (5,000 lamports per signature) + Priority feePriority fee = Compute units x Price per unit (micro-lamports per compute unit)
实际费用示例:
- 简单的 USDC 转账:正常情况下约 $0.001-0.005
- 拥堵时:约 $0.01-0.05
- 高峰拥堵:可能更高
示例实现:
@solana-program/compute-budget
包提供了一个辅助函数,可以轻松地为交易更新或添加计算单元价格(以微 lamports 计)的指令。
import { updateOrAppendSetComputeUnitPriceInstruction } from "@solana-program/compute-budget";const tx = pipe(createTransactionMessage({ version: 0 }),(m) => setTransactionMessageFeePayerSigner(payer, m),(m) => setTransactionMessageLifetimeUsingBlockhash(latestBlockhash, m),(m) => appendTransactionMessageInstructions([myInstructions], m),(m) => updateOrAppendSetComputeUnitPriceInstruction(1000n as MicroLamports, m));
获取费用估算: 大多数 RPC 服务商都提供优先费用 API:
完整的费用机制请参见 Transaction Fees 以及我们的指南:如何为交易添加优先费用。
优化计算单元
在 Solana 上,计算量实际上衡量的是程序执行的工作量。每笔交易都有计算单元的上限(目前为 140 万计算单元),每个账户每个区块也有计算单元的上限(目前为 1 亿计算单元)。
提交交易时,你需要预估将会用到的计算单元数量,并相应设置计算单元上限——这实际上是请求为你的交易预留多少总容量。实际操作中,准确预估交易所需的计算单元对于交易能否被打包进区块至关重要(同时也影响优先费用的管理)。
Solana JSON RPC API 提供了一个
simulatetransaction
方法,可用于估算交易所需的计算单元,其中包括对将使用的计算单元的估算。
@solana/kit
提供了辅助函数,可在一步内估算交易的资源限制并将其设置在消息中(底层使用
simulatetransaction
方法)。对于版本 1 的交易,这些辅助函数还会估算已加载账户的数据大小限制。
import {estimateResourceLimitsFactory,estimateAndSetResourceLimitsFactory} from "@solana/kit";const estimateResourceLimits = estimateResourceLimitsFactory({ rpc });const estimateAndSetResourceLimits = estimateAndSetResourceLimitsFactory(estimateResourceLimits);const txWithResourceLimits = await estimateAndSetResourceLimits(tx);
如果您使用 kit
插件客户端构建并发送交易,通常无需此步骤——客户端在发送时会自动为您添加计算预算指令(.sendTransaction())。上述手动流程适用于直接使用
@solana/kit 组装交易的场景。
在生产环境中,如果您需要多次重复执行相同类型的交易,建议缓存该交易类型的计算量估算值,以避免每次都重新估算计算单元所带来的额外开销。
Jito 捆绑包
Jito 捆绑包是一种用于管理多笔交易原子性执行的工具。其实现方式是向 Jito 网络发送多笔交易并附带小费。小费可用于激励 Jito 网络将您的交易纳入区块中。
相关资源:
重试策略
交易可能因多种原因而失败。与传统支付 API 立即返回成功或失败不同,区块链交易需要持续跟踪确认状态。
核心概念:
- 区块哈希过期:交易有效期约为 ~150 个区块(约 60–90 秒)
- 幂等性:相同的已签名交易始终产生相同的签名——重新提交是安全的
- 指数退避:避免频繁重试对网络造成过大压力
import {createSolanaRpc,createSolanaRpcSubscriptions,sendAndConfirmTransactionFactory,isSolanaError,SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED} from "@solana/kit";const rpc = createSolanaRpc(process.env.RPC_URL!);const rpcSubscriptions = createSolanaRpcSubscriptions(process.env.RPC_WSS_URL!);const sendAndConfirmTransaction = sendAndConfirmTransactionFactory({rpc,rpcSubscriptions});// Send with automatic confirmation tracking and block height monitoringtry {await sendAndConfirmTransaction(signedTransaction, {commitment: "confirmed",// Optional: abort after 75 secondsabortSignal: AbortSignal.timeout(75_000)});} catch (e) {if (isSolanaError(e, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED)) {// Blockhash expired—rebuild transaction with fresh blockhash and retryrebuildAndRetryTransaction(); // implement your own logic for rebuilding and retrying the transaction}throw e;}
sendAndConfirmTransactionFactory 来自
@solana/kit,可自动处理确认轮询和区块高度跟踪。当交易的 blockhash 过期时,它会抛出
SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,提示您需要使用新的 blockhash 重新构建交易。
其他资源
了解确认级别
Solana 提供三种确认级别。以传统金融术语来表述:
| 级别 | Solana 定义 | 传统等价概念 | 适用场景 |
|---|---|---|---|
processed | 已打包入块,尚未投票 | 待授权 | 实时 UI 更新 |
confirmed | 超级多数已投票 | 资金已清算 | 大多数支付场景 |
finalized | 已固化,不可逆 | 资金已结算 | 高价值交易、合规场景 |
各级别的使用时机:
- UI 更新:显示
processed以提供即时反馈("支付已提交") - 为用户账户充值:等待
confirmed(适用于大多数交易) - 发货实物商品:等待
finalized - 大额提现:等待
finalized - 合规/审计:始终记录
finalized状态
有关检查交易状态的更多信息,请参阅 与 Solana 交互。
错误处理
Solana Kit 通过 isSolanaError()
提供类型化错误。请使用具体的错误码而非字符串匹配:
import {isSolanaError,SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED,SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE,SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND,SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS} from "@solana/kit";function handlePaymentError(error: unknown): {message: string;retryable: boolean;} {// Blockhash expired—rebuild and retryif (isSolanaError(error, SOLANA_ERROR__BLOCK_HEIGHT_EXCEEDED) ||isSolanaError(error, SOLANA_ERROR__TRANSACTION_ERROR__BLOCKHASH_NOT_FOUND)) {return { message: "Transaction expired—rebuilding", retryable: true };}// Insufficient SOL for feesif (isSolanaError(error,SOLANA_ERROR__TRANSACTION_ERROR__INSUFFICIENT_FUNDS_FOR_FEE)) {return { message: "Not enough SOL for fees", retryable: false };}// Insufficient token balanceif (isSolanaError(error, SOLANA_ERROR__INSTRUCTION_ERROR__INSUFFICIENT_FUNDS)) {return { message: "Insufficient balance", retryable: false };}// Unknown errorconsole.error("Payment error:", error);return { message: "Payment failed—please retry", retryable: true };}
常见错误码:
| 错误码 | 原因 | 恢复方式 |
|---|---|---|
BLOCK_HEIGHT_EXCEEDED | Blockhash 已过期 | 使用新的 blockhash 重新构建交易 |
BLOCKHASH_NOT_FOUND | Blockhash 未找到 | 使用新的 blockhash 重新构建交易 |
INSUFFICIENT_FUNDS_FOR_FEE | SOL 余额不足 | 为付费账户充值或使用费用抽象 |
INSUFFICIENT_FUNDS | 代币余额不足 | 用户需要增加余额 |
ACCOUNT_NOT_FOUND | token account 不存在 | 在交易中创建 ATA |
无Gas交易
用户希望使用稳定币支付,而不是为网络费用购买SOL。无Gas交易解决了这一问题——类似于Venmo用户无需关心ACH手续费。完整实现方式请参阅费用抽象。
安全性
密钥管理
- 切勿在前端代码中暴露私钥。 请使用后端签名、硬件钱包、多签钱包或密钥管理服务。
- 分离热钱包与冷钱包。 热钱包用于日常操作,冷钱包用于资金储备。
- 备份所有生产环境密钥。 将加密备份存储在多个安全位置。丢失密钥意味着永久失去访问权限。
- 为开发网和主网使用不同的密钥。 开发网密钥不应与主网密钥相同。使用基于环境的配置,确保每个网络加载对应的正确密钥。
签名基础设施
对于生产环境的后端签名,推荐使用 Keychain——这是一个统一的签名库,通过单一接口抽象多种密钥管理后端,支持:Memory、Vault、Privy、Turnkey、AWS KMS、Fireblocks、GCP KMS、CDP、Para、Dfns、Crossmint、Openfort 和 Utila。这使您可以在本地使用内存密钥进行开发,然后无需修改应用代码即可切换到生产级后端。
不确定哪种后端适合您?请参阅选择后端。Keychain 同时支持 Rust 和 TypeScript。
RPC 安全
将 RPC 端点视为 API 密钥——不要在前端代码中暴露它们,因为它们可能被提取并滥用。使用后端代理或环境变量,确保不会被打包到客户端代码中。
监控
在生产环境中跟踪以下指标:
| 指标 | 原因 |
|---|---|
| 交易成功率 | 及早发现问题 |
| 确认延迟 | 监控网络健康状况 |
| 优先费用支出 | 成本管理 |
| RPC 错误率 | 提供商健康状况 |
为以下情况设置警报:
- 超过阈值的资金库转账
- 失败交易率激增
- 异常的接收方模式
- RPC 错误率上升
要了解大规模实时交易监控,请参阅我们的索引指南。
验证地址
每个代币和程序在主网上都有唯一正确的地址。模仿 USDC 或其他稳定币的欺诈代币很常见——它们会有相同的名称和符号,但铸币地址不同。您的应用程序应该硬编码或将铸币地址加入白名单(根据您的要求),绝不要从不可信来源动态接受它们。
基于环境的配置: Devnet 和 Mainnet 通常使用完全不同的代币铸造地址。请配置您的应用程序以根据不同环境加载正确的地址——不要硬编码主网地址并在测试时忘记切换,更糟糕的是,将开发网地址发布到生产环境。
一些常见的稳定币铸造地址包括:
| 代币 | 发行方 | 铸造地址 |
|---|---|---|
| USDC | Circle | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| USDT | Tether | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB |
| PYUSD | PayPal | 2b1kV6DkPAnxd5ixfnxCpjxmKwqjjaYmCZfHsFu24GXo |
| USDG | Paxos | 2u1tszSeqZ3qBWF3uNGPFc8TzMk2tdiwknnRMWGWjGWH |
程序地址同样重要。向错误的程序发送指令将导致失败——或者更糟的是,造成资金不可逆转的损失。Token Program 的地址为:
| 程序 | 地址 |
|---|---|
| Token Program | TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA |
| Token-2022 Program | TokenzQdBNbLqP5VEhdkAS6EPFLC1PHnBqCXEpPxuEb |
将正确的地址加入白名单可防止伪造代币,但无法防止发送到错误类型的账户。收款地址可以是钱包、token account、铸币账户或程序,每种类型需要不同的处理方式。将原生 SOL 发送到钱包以外的任何地址都会使资金脱离发送方的控制——发送到铸币账户或程序将直接丢失,发送到 token account 则只能由账户所有者找回——且不会有交易失败的提示来警告用户。
发送前验证收款方
在签署转账之前,请对每个收款地址进行分类。请参阅验证地址,获取完整的决策树和参考代码,用于区分钱包、token account、铸币账户和程序。
上线前检查清单
- 已获取主网 SOL 用于手续费和 rent
- 已配置生产环境 RPC(非公共端点)
- 已配置备用 RPC 端点
- 已实现动态定价的优先费用
- 重试逻辑可处理区块哈希过期问题
- 确认级别与使用场景相匹配
- 所有常见错误均已妥善处理
- 已配置无 Gas 模式(如适用)
- 已验证主网代币地址(非开发网铸币账户)
- 发送前已验证收款地址(钱包 vs token account vs 铸币账户 vs 程序)
- 所有密钥已安全备份
- 已审查密钥管理方案(前端不存储密钥)
- 交易监控与告警已启用
- 已按预期负载完成压力测试
部署程序
如果您正在部署自定义 Solana 程序作为支付基础设施的一部分,则需要考虑一些额外的事项。
部署前准备
- Solana CLI 版本: 确保您使用的是最新版本的 Solana CLI。
- 程序 keypair:
您的程序在主网上的地址与开发网(devnet)不同(除非您重用相同的 keypair)。请更新应用配置中的所有相关引用。将您的程序 keypair 存储在安全位置(请注意,运行
cargo clean可能会删除您的程序 keypair)。 - 初始化账户: 如果您的程序需要管理员账户、PDA 或其他状态账户,请确保在用户与您的应用交互之前,这些账户已在主网上创建完毕。程序所需的任何 Associated Token Accounts(ATA)同样如此。
部署流程
- 缓冲账户: 大型程序通过缓冲账户进行部署。
solana program deploy命令会自动处理此过程,但请注意部署并非原子操作——若部署中断,您可能需要恢复或关闭缓冲账户。请参阅 部署程序。 - 升级权限: 决定您的程序在上线后是否支持升级。若要实现不可变性,请在部署后撤销升级权限;若需保留灵活性,请妥善保管升级权限密钥。
- 租金: 确保您的部署钱包中有足够的 SOL,以覆盖所有 program account 的免租最低余额要求。
- 验证: 验证您的程序,确保您部署到 Solana 网络上的可执行程序与代码仓库中的源代码一致。
有关程序部署的完整指南,请参阅 部署程序。
Is this page helpful?