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.

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:
- Two-step approval: The
approve+transferFrompattern requires two transactions - 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:
| Concept | Description |
|---|---|
| Mint | The token contract (e.g., USDC mint) |
| Token Account | A user’s balance for a specific token |
| Associated Token Account (ATA) | The canonical token account derived from wallet + mint |
| Authority | The 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
| Aspect | EVM + ERC-3009 | Solana |
|---|---|---|
| Approval pattern | Off-chain signature | Built into transaction |
| Gas payment | Facilitator pays | Configurable fee payer |
| Nonce handling | Random bytes32 | Recent blockhash |
| Transaction atomicity | Single operation | Multi-instruction |
| Meta-transaction support | Requires ERC-3009 | Native |
| Delegate pattern | transferWithAuthorization | Approve + Transfer |
Why Both Approaches Work for x402
Despite architectural differences, both achieve the same x402 goals:
- User doesn’t pay gas: Facilitator covers fees
- Single user action: One signature/approval
- Trustless verification: Cryptographic proof of intent
- 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:
| Property | Value |
|---|---|
| Mint Address | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v |
| Decimals | 6 |
| Program | SPL Token (not Token-2022) |
| Issuer | Circle |
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;
}
Related Articles
- ERC-3009: The Protocol Behind x402 Payments - The EVM equivalent
- x402 Facilitators - Multi-chain settlement infrastructure
- x402 Developer’s Guide - Build your first paid API
- Why x402 Doesn’t Support USDT - Token compatibility explained
References
- Solana Token Program Documentation
- Token-2022 Extensions
- Solana EIP-2612 Equivalent
- Circle USDC on Solana
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.