x402 Solana USDC SPL tokens Token-2022

Solana's Authorization Mechanism: How x402 Works Beyond EVM

Understanding how Solana's native architecture enables x402 payments without needing ERC-3009, and how Token-2022 extensions enhance the experience.

PayIn Team | | 7 min read

Solana Authorization Mechanism

While ERC-3009 powers x402 payments on EVM chains, Solana takes a fundamentally different approach. Its native architecture already solves many problems that ERC-3009 was designed to address—but it introduces its own unique patterns for token authorization.

Why Solana Doesn’t Need ERC-3009

On Ethereum, ERC-3009 exists to solve two major pain points:

  1. Two-step approval: The approve + transferFrom pattern requires two transactions
  2. Gas payment: Users must hold ETH to pay for gas

Solana’s architecture inherently addresses both:

Atomic Multi-Instruction Transactions

A single Solana transaction can contain multiple instructions that execute atomically:

const transaction = new Transaction()
  .add(createApproveInstruction(...))  // Approve
  .add(createTransferInstruction(...)); // Transfer

// Both execute in ONE atomic transaction
await sendAndConfirmTransaction(connection, transaction, [payer]);

No separate approval transaction needed. Either everything succeeds, or everything fails.

Fee Payer Abstraction

On Solana, any account can pay the transaction fee—not just the signer:

const transaction = new Transaction();
transaction.feePayer = sponsorPublicKey; // Sponsor pays gas
transaction.add(transferInstruction);

// User signs for authorization, sponsor signs for fee payment
const signedTx = await userWallet.signTransaction(transaction);
await sponsorWallet.signTransaction(signedTx);

This enables “gasless” experiences where users never need to hold SOL.

Solana’s Token Authorization Model

SPL Token Program Basics

Solana’s SPL Token Program handles all fungible token operations. Key concepts:

ConceptDescription
MintThe token contract (e.g., USDC mint)
Token AccountA user’s balance for a specific token
Associated Token Account (ATA)The canonical token account derived from wallet + mint
AuthorityThe account that can authorize transfers

Transfer Authorization

Only the owner or an approved delegate can authorize transfers:

// Direct owner transfer
const transferIx = createTransferInstruction(
  sourceATA,        // Source token account
  destinationATA,   // Destination token account
  ownerPublicKey,   // Authority (owner)
  amount
);

// Delegated transfer (after approval)
const transferIx = createTransferCheckedInstruction(
  sourceATA,
  mintAddress,
  destinationATA,
  delegatePublicKey, // Authority (delegate)
  amount,
  decimals
);

Delegate Approval Pattern

While not needed for simple transfers, delegation exists for DeFi use cases:

// Approve a delegate to spend up to 1000 tokens
const approveIx = createApproveInstruction(
  tokenAccount,
  delegatePublicKey,
  ownerPublicKey,
  1000_000_000n // Amount in base units
);

Unlike ERC-20, Solana doesn’t require this for third-party-initiated transfers in x402.

x402 on Solana: The Flow

In x402, Solana payments work differently from EVM chains:

┌─────────────┐                      ┌─────────────┐
│   Client    │ ─── 1. Request ────► │   Server    │
└─────────────┘                      └──────┬──────┘
       │                                    │
       │                                    │ 2. 402 + Requirements
       │                                    ▼
       │                             ┌─────────────┐
       │ ◄─────────────────────────  │  Payment    │
       │                             │  Details    │
       │                             └─────────────┘

       │ 3. Sign transaction message

┌─────────────┐
│  Signature  │
└──────┬──────┘

       │ 4. X-PAYMENT with signed payload

┌─────────────┐                      ┌─────────────┐
│   Server    │ ─── 5. Verify ─────► │ Facilitator │
└──────┬──────┘                      └──────┬──────┘
       │                                    │
       │ ◄─────── 6. Valid ────────────────┘

       │ 7. Response

┌─────────────┐                      ┌─────────────┐
│   Client    │      8. Settle ────► │ Facilitator │
└─────────────┘                      │ (submits tx)│
                                     └─────────────┘

The Solana x402 Payload

{
  "x402Version": 1,
  "scheme": "exact",
  "network": "solana",
  "payload": {
    "signature": "base58-encoded-signature",
    "transaction": {
      "from": "SenderPublicKey",
      "to": "RecipientPublicKey",
      "amount": "1000000",
      "mint": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"
    }
  }
}

The Facilitator constructs and submits the actual transaction using the signed authorization.

Token-2022: Enhanced Capabilities

Solana’s Token-2022 program (Token Extensions) adds powerful features relevant to x402:

Transfer Hooks

Custom logic that executes on every transfer:

// A program can register a transfer hook
// that runs additional validation or side effects
pub fn transfer_hook(
    ctx: Context<TransferHook>,
    amount: u64,
) -> Result<()> {
    // Custom validation, logging, or state updates
    Ok(())
}

Use cases for x402:

  • Compliance checks: Verify both parties are KYC’d
  • Rate limiting: Enforce per-address transfer limits
  • Audit logging: Record all transfers for compliance

Required Memo Extension

Forces a memo with every transfer:

// Enable required memo on a token account
const enableMemoIx = createEnableRequiredMemoTransfersInstruction(
  tokenAccount,
  owner
);

// Now all incoming transfers must include a memo
const transferIx = createTransferInstruction(...);
const memoIx = createMemoInstruction("Payment for API access", []);

// Both must be in the same transaction
const tx = new Transaction().add(memoIx).add(transferIx);

This is valuable for:

  • Payment references: Link payments to invoices
  • Compliance: Satisfy regulatory memo requirements
  • Debugging: Trace payment purposes

Comparing EVM (ERC-3009) vs Solana

AspectEVM + ERC-3009Solana
Approval patternOff-chain signatureBuilt into transaction
Gas paymentFacilitator paysConfigurable fee payer
Nonce handlingRandom bytes32Recent blockhash
Transaction atomicitySingle operationMulti-instruction
Meta-transaction supportRequires ERC-3009Native
Delegate patterntransferWithAuthorizationApprove + Transfer

Why Both Approaches Work for x402

Despite architectural differences, both achieve the same x402 goals:

  1. User doesn’t pay gas: Facilitator covers fees
  2. Single user action: One signature/approval
  3. Trustless verification: Cryptographic proof of intent
  4. Atomic settlement: All-or-nothing execution

Gasless Transfers on Solana

Several approaches enable gasless USDC transfers:

1. Fee Payer Sponsorship (Native)

const transaction = new Transaction();
transaction.feePayer = facilitatorPublicKey;
transaction.add(usdcTransferInstruction);

// User signs transfer authorization
// Facilitator signs as fee payer and submits

2. Kora Protocol

A service that converts SPL token fees to SOL:

User pays fee in USDC → Kora converts to SOL → Validator receives SOL

From the user’s perspective, they pay in USDC and never touch SOL.

3. Octane

Open-source gasless transaction relayer:

// User creates and signs transaction
// Octane accepts token payment and relays with SOL fee
const response = await fetch("https://octane.example.com/transfer", {
  method: "POST",
  body: JSON.stringify({ transaction: signedTx }),
});

USDC on Solana

The native USDC SPL token:

PropertyValue
Mint AddressEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
Decimals6
ProgramSPL Token (not Token-2022)
IssuerCircle
import { getAssociatedTokenAddress } from "@solana/spl-token";

const USDC_MINT = new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v");

// Get user's USDC token account
const userUsdcAta = await getAssociatedTokenAddress(
  USDC_MINT,
  userWallet.publicKey
);

Implementation Example

Creating a Solana USDC transfer for x402:

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 // Convert to base units (6 decimals)
  );

  const transaction = new Transaction();
  transaction.add(transferIx);
  transaction.feePayer = feePayer;
  transaction.recentBlockhash = (
    await connection.getLatestBlockhash()
  ).blockhash;

  return transaction;
}

References


Solana’s architecture makes gasless, authorized transfers a first-class feature. While EVM chains need ERC-3009 to achieve similar functionality, Solana delivers it natively—making it an excellent fit for x402 payments.