Solana 的授权转账机制:x402 如何在非 EVM 链上运作
深入理解 Solana 原生架构如何无需 ERC-3009 即可实现 x402 支付,以及 Token-2022 扩展如何增强支付体验。

在 EVM 链上,ERC-3009 驱动着 x402 支付;而 Solana 采用了完全不同的方式。其原生架构已经解决了 ERC-3009 旨在解决的许多问题,但同时引入了独特的代币授权模式。
为什么 Solana 不需要 ERC-3009
在以太坊上,ERC-3009 的存在是为了解决两大痛点:
- 两步授权:
approve+transferFrom模式需要两笔交易 - Gas 支付:用户必须持有 ETH 来支付 Gas
Solana 的架构天然解决了这两个问题:
原子化多指令交易
单个 Solana 交易可以包含多个原子执行的指令:
const transaction = new Transaction()
.add(createApproveInstruction(...)) // 授权
.add(createTransferInstruction(...)); // 转账
// 两者在一个原子交易中执行
await sendAndConfirmTransaction(connection, transaction, [payer]);
无需单独的授权交易。要么全部成功,要么全部失败。
手续费支付者抽象
在 Solana 上,任何账户都可以支付交易手续费——不仅限于签名者:
const transaction = new Transaction();
transaction.feePayer = sponsorPublicKey; // 赞助者支付 Gas
transaction.add(transferInstruction);
// 用户签名授权,赞助者签名支付手续费
const signedTx = await userWallet.signTransaction(transaction);
await sponsorWallet.signTransaction(signedTx);
这使得用户无需持有 SOL 即可获得「无 Gas」体验。
Solana 的代币授权模型
SPL 代币程序基础
Solana 的 SPL 代币程序处理所有同质化代币操作。核心概念:
| 概念 | 描述 |
|---|---|
| Mint | 代币合约(如 USDC mint) |
| Token Account | 用户针对特定代币的余额账户 |
| Associated Token Account (ATA) | 由钱包 + mint 派生的规范代币账户 |
| Authority | 可以授权转账的账户 |
转账授权
只有所有者或已批准的代理可以授权转账:
// 所有者直接转账
const transferIx = createTransferInstruction(
sourceATA, // 源代币账户
destinationATA, // 目标代币账户
ownerPublicKey, // 授权方(所有者)
amount
);
// 代理转账(需先授权)
const transferIx = createTransferCheckedInstruction(
sourceATA,
mintAddress,
destinationATA,
delegatePublicKey, // 授权方(代理)
amount,
decimals
);
代理授权模式
虽然简单转账不需要,但 DeFi 场景中存在代理模式:
// 授权代理最多花费 1000 个代币
const approveIx = createApproveInstruction(
tokenAccount,
delegatePublicKey,
ownerPublicKey,
1000_000_000n // 基础单位的数量
);
与 ERC-20 不同,Solana 在 x402 的第三方发起转账中并不需要这个模式。
x402 在 Solana 上的流程
在 x402 中,Solana 支付的工作方式与 EVM 链不同:
┌─────────────┐ ┌─────────────┐
│ 客户端 │ ─── 1. 请求 ───────► │ 服务器 │
└─────────────┘ └──────┬──────┘
│ │
│ │ 2. 402 + 支付要求
│ ▼
│ ┌─────────────┐
│ ◄───────────────────────── │ 支付详情 │
│ └─────────────┘
│
│ 3. 签署交易消息
▼
┌─────────────┐
│ 签名 │
└──────┬──────┘
│
│ 4. X-PAYMENT 携带签名载荷
▼
┌─────────────┐ ┌─────────────┐
│ 服务器 │ ─── 5. 验证 ───────► │ Facilitator │
└──────┬──────┘ └──────┬──────┘
│ │
│ ◄─────── 6. 有效 ─────────────────┘
│
│ 7. 响应
▼
┌─────────────┐ ┌─────────────┐
│ 客户端 │ 8. 结算 ───────► │ Facilitator │
└─────────────┘ │ (提交交易) │
└─────────────┘
Solana x402 载荷
{
"x402Version": 1,
"scheme": "exact",
"network": "solana",
"payload": {
"signature": "base58-encoded-signature",
"transaction": {
"from": "SenderPublicKey",
"to": "RecipientPublicKey",
"amount": "1000000",
"mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
}
}
}
Facilitator 使用签名授权构建并提交实际交易。
Token-2022:增强功能
Solana 的 Token-2022 程序(代币扩展)添加了与 x402 相关的强大功能:
Transfer Hooks(转账钩子)
在每次转账时执行自定义逻辑:
// 程序可以注册一个转账钩子
// 运行额外的验证或副作用
pub fn transfer_hook(
ctx: Context<TransferHook>,
amount: u64,
) -> Result<()> {
// 自定义验证、日志记录或状态更新
Ok(())
}
x402 的应用场景:
- 合规检查:验证双方是否已完成 KYC
- 速率限制:强制执行每地址转账限额
- 审计日志:为合规记录所有转账
Required Memo 扩展(必须备注)
强制每次转账必须附带备注:
// 在代币账户上启用必须备注
const enableMemoIx = createEnableRequiredMemoTransfersInstruction(
tokenAccount,
owner
);
// 现在所有入账转账必须包含备注
const transferIx = createTransferInstruction(...);
const memoIx = createMemoInstruction("Payment for API access", []);
// 两者必须在同一交易中
const tx = new Transaction().add(memoIx).add(transferIx);
这对以下场景很有价值:
- 支付引用:将支付关联到发票
- 合规:满足监管备注要求
- 调试:追踪支付用途
EVM (ERC-3009) vs Solana 对比
| 方面 | EVM + ERC-3009 | Solana |
|---|---|---|
| 授权模式 | 链下签名 | 内置于交易 |
| Gas 支付 | Facilitator 支付 | 可配置费用支付者 |
| Nonce 处理 | 随机 bytes32 | 最新区块哈希 |
| 交易原子性 | 单一操作 | 多指令 |
| 元交易支持 | 需要 ERC-3009 | 原生支持 |
| 代理模式 | transferWithAuthorization | Approve + Transfer |
为什么两种方式都适用于 x402
尽管架构不同,两者都实现了相同的 x402 目标:
- 用户无需支付 Gas:Facilitator 承担费用
- 用户单次操作:一次签名/授权
- 无需信任的验证:意图的加密证明
- 原子结算:全部成功或全部失败
Solana 上的无 Gas 转账
多种方式可实现无 Gas 的 USDC 转账:
1. 费用支付者赞助(原生)
const transaction = new Transaction();
transaction.feePayer = facilitatorPublicKey;
transaction.add(usdcTransferInstruction);
// 用户签署转账授权
// Facilitator 作为费用支付者签署并提交
2. Kora 协议
将 SPL 代币费用转换为 SOL 的服务:
用户以 USDC 支付费用 → Kora 转换为 SOL → 验证者收到 SOL
从用户角度,他们以 USDC 支付,从不接触 SOL。
3. Octane
开源无 Gas 交易中继器:
// 用户创建并签署交易
// Octane 接受代币支付并以 SOL 费用中继
const response = await fetch("https://octane.example.com/transfer", {
method: "POST",
body: JSON.stringify({ transaction: signedTx }),
});
Solana 上的 USDC
原生 USDC SPL 代币:
| 属性 | 值 |
|---|---|
| Mint 地址 | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| 精度 | 6 |
| 程序 | SPL Token(非 Token-2022) |
| 发行方 | Circle |
import { getAssociatedTokenAddress } from "@solana/spl-token";
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
// 获取用户的 USDC 代币账户
const userUsdcAta = await getAssociatedTokenAddress(
USDC_MINT,
userWallet.publicKey
);
实现示例
为 x402 创建 Solana USDC 转账:
import {
Connection,
PublicKey,
Transaction,
SystemProgram,
} from "@solana/web3.js";
import {
getAssociatedTokenAddress,
createTransferInstruction,
TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");
async function createPaymentTransaction(
connection: Connection,
payer: PublicKey,
recipient: PublicKey,
amount: number,
feePayer: PublicKey
): Promise<Transaction> {
const payerAta = await getAssociatedTokenAddress(USDC_MINT, payer);
const recipientAta = await getAssociatedTokenAddress(USDC_MINT, recipient);
const transferIx = createTransferInstruction(
payerAta,
recipientAta,
payer,
amount * 1_000_000 // 转换为基础单位(6 位精度)
);
const transaction = new Transaction();
transaction.add(transferIx);
transaction.feePayer = feePayer;
transaction.recentBlockhash = (
await connection.getLatestBlockhash()
).blockhash;
return transaction;
}
相关文章
- ERC-3009:x402 支付背后的协议 - EVM 的等效方案
- x402 Facilitator - 多链结算基础设施
- x402 开发者指南 - 构建你的第一个付费 API
- 为什么 x402 不支持 USDT - 代币兼容性解析
参考资料
Solana 的架构使无 Gas、授权转账成为一等公民功能。EVM 链需要 ERC-3009 来实现类似功能,而 Solana 原生支持——这使其非常适合 x402 支付。