Skip to main content

Overview

The Solana payment handler implements the exact scheme for x402-exact payments on Solana-based networks (Mainnet, Devnet, Testnet). It uses partially-signed transactions where the client and facilitator co-sign a single transaction.

Supported Networks

  • solana-mainnet-beta - Solana Mainnet Beta
  • solana-devnet - Solana Devnet
  • solana-testnet - Solana Testnet

Payment Mechanism

Partially-Signed Transactions

In Solana x402-exact payments:
  1. Client creates transaction: Builds a transaction that transfers SPL tokens from their wallet to the merchant
  2. Client signs partially: Signs the transaction with their wallet key
  3. Facilitator co-signs: Adds its signature as the fee payer
  4. Facilitator submits: Sends the fully-signed transaction to Solana
This approach allows the facilitator to sponsor transaction fees while the client authorizes the token transfer.

Fee Payer Role

The facilitator acts as the fee payer for all Solana transactions:
  • Pays network transaction fees (varies by network conditions)
  • Pays priority fees if specified in the transaction
  • Must maintain a SOL balance to cover fees
The client only needs tokens (e.g., USDC) to pay the merchant - no SOL required.

Requirements Enrichment

When a resource server calls /accepts, the Solana handler adds these fields to the extra object:
feePayer
string
required
The facilitator’s public key that will co-sign as fee payer.Example: "9WzDXwBbmkg8ZTbNMqUxvQRAyrZzDsGYdLVL9zYtAWWM"
decimals
number
Token decimals for the specified asset mint. Used for amount calculations.Example: 6 for USDC
recentBlockhash
string
Recent blockhash for transaction validity. Transactions are only valid for ~60 seconds.Example: "4sGjMW1sUnHzSxGspuhpqLDx6wiyjNtZ..."

Settlement Validation

When processing a /settle request, the Solana handler performs extensive validation:

1. Fee Payer Verification

Ensures the transaction’s fee payer matches the facilitator’s public key:
if (transactionMessage.feePayer.address !== extra.feePayer) {
  throw new Error("Fee payer mismatch");
}

2. Compute Budget Validation

Validates maximum priority fee to prevent excessive costs:
  • Maximum priority fee ≤ 100,000 lamports (configurable)
  • Priority fee is calculated as: (compute units × price in micro-lamports) / 1,000,000
This prevents clients from tricking the facilitator into paying excessive fees.

3. Transfer Instruction Validation

Verifies the SPL token transfer instruction:
// Parse the transfer instruction
const transfer = parseTransferCheckedInstruction(instruction);

// Verify transfer amount matches requirements
if (transfer.data.amount !== BigInt(paymentRequirements.maxAmountRequired)) {
  throw new Error("Invalid transfer amount");
}

// Verify correct token mint
if (transfer.accounts.mint.address !== paymentRequirements.asset) {
  throw new Error("Invalid token mint");
}

// Verify recipient is the merchant
if (transfer.accounts.destination.address !== destination) {
  throw new Error("Invalid recipient");
}

// Verify authority is not the facilitator (prevents fund theft)
if (transfer.accounts.authority.address === facilitatorAddress) {
  throw new Error("Transfer authority cannot be facilitator");
}

4. Security Checks

Prevents facilitator fund theft:
  • Ensures source token account is NOT owned by facilitator
  • Verifies no other instructions attempt to drain facilitator accounts
  • Checks authority fields point to client, not facilitator

Transaction Execution

After validation, the facilitator:
  1. Signs transaction: Adds facilitator signature as fee payer
  2. Simulates transaction: Validates transaction will succeed on-chain
  3. Submits to RPC: Sends transaction to Solana network
  4. Polls for confirmation: Checks transaction status until confirmed or finalized
  5. Returns result: Provides transaction signature and network ID
// Simulate first to catch errors
const simResult = await rpc.simulateTransaction(base64EncodedTransaction, {
  encoding: "base64",
}).send();

if (simResult.value.err) {
  return { success: false, error: "Transaction simulation failed" };
}

// Send transaction
const signature = await rpc.sendTransaction(base64EncodedTransaction, {
  encoding: "base64",
}).send();

// Poll for confirmation
const status = await rpc.getSignatureStatuses([signature]).send();
if (status.value[0]?.confirmationStatus === "confirmed" ||
    status.value[0]?.confirmationStatus === "finalized") {
  return {
    success: true,
    txHash: signature,
    networkId: 'solana-devnet',
    error: null
  };
}

Token Support

The Solana handler supports any SPL token, including:
  • USDC: EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v (Mainnet)
  • Custom tokens: Any SPL token mint address
Token decimals are automatically fetched from on-chain token mint data.

Error Scenarios

Common errors during Solana payment processing:
ErrorCauseSolution
Fee payer mismatchTransaction fee payer isn’t facilitatorClient must use facilitator’s feePayer from requirements
Invalid transfer amountAmount doesn’t match requirementsVerify amount calculation matches token decimals
Transaction simulation failedTransaction would fail on-chainCheck token accounts exist and have sufficient balance
Blockhash expiredRecent blockhash too old (>60s)Client must fetch fresh requirements before payment
Compute budget exceededCompute units or priority fee too highReduce compute limits to reasonable values