x402 Solana USDC SPL 代币 Token-2022

Solana 的授权转账机制:x402 如何在非 EVM 链上运作

深入理解 Solana 原生架构如何无需 ERC-3009 即可实现 x402 支付,以及 Token-2022 扩展如何增强支付体验。

PayIn Team | | 25 分钟阅读

Solana Authorization Mechanism

在 EVM 链上,ERC-3009 驱动着 x402 支付;而 Solana 采用了完全不同的方式。其原生架构已经解决了 ERC-3009 旨在解决的许多问题,但同时引入了独特的代币授权模式。

为什么 Solana 不需要 ERC-3009

在以太坊上,ERC-3009 的存在是为了解决两大痛点:

  1. 两步授权approve + transferFrom 模式需要两笔交易
  2. 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-3009Solana
授权模式链下签名内置于交易
Gas 支付Facilitator 支付可配置费用支付者
Nonce 处理随机 bytes32最新区块哈希
交易原子性单一操作多指令
元交易支持需要 ERC-3009原生支持
代理模式transferWithAuthorizationApprove + Transfer

为什么两种方式都适用于 x402

尽管架构不同,两者都实现了相同的 x402 目标:

  1. 用户无需支付 Gas:Facilitator 承担费用
  2. 用户单次操作:一次签名/授权
  3. 无需信任的验证:意图的加密证明
  4. 原子结算:全部成功或全部失败

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;
}

相关文章

参考资料


Solana 的架构使无 Gas、授权转账成为一等公民功能。EVM 链需要 ERC-3009 来实现类似功能,而 Solana 原生支持——这使其非常适合 x402 支付。