Skip to content

Instantly share code, notes, and snippets.

@ngundotra
Last active July 3, 2025 14:01
Show Gist options
  • Save ngundotra/568358ac2b30c91370d6bd80b354cd6b to your computer and use it in GitHub Desktop.
Save ngundotra/568358ac2b30c91370d6bd80b354cd6b to your computer and use it in GitHub Desktop.
Solana x402 Permanent Delegate

x402 on Solana: Permanent Delegate Flow

Flow Diagram

sequenceDiagram
    participant User as User<br/>(Has: Wallet, xUSDC)<br/>(Knows: Sign messages)
    participant Client as Client App<br/>(x402 SDK)
    participant Server as Resource Server<br/>(Has: Solana wallet)<br/>(Knows: Basic web)
    participant Facilitator as Facilitator<br/>(Has: Permanent Delegate)<br/>(Knows: Solana txs)
    participant Solana as Solana<br/>(xUSDC Program)

    Note over User,Solana: ONE-TIME SETUP (Once per user lifetime)
    User->>Solana: Deposit USDC, get xUSDC
    Note over User: User now has xUSDC in wallet

    Note over User,Solana: PAYMENT FLOW (Every request)
    
    %% Step 1: Initial Request
    User->>Client: "Get premium content"
    Client->>Server: GET /premium-article
    Server-->>Client: 402 Payment Required<br/>X-PAYMENT-REQUIRED: {<br/>  payTo: "server_solana_wallet",<br/>  amount: "0.001 xUSDC",<br/>  nonce: "abc123"<br/>}
    
    %% Step 2: Client creates signature
    Note over Client: Client SDK handles all complexity
    Client->>User: Sign message (NOT transaction):<br/>"Pay 0.001 xUSDC to server_wallet<br/>for nonce abc123"
    User-->>Client: Ed25519 signature
    
    %% Step 3: Retry with payment
    Client->>Server: GET /premium-article<br/>X-PAYMENT: base64(signature + params)
    
    %% Step 4: Server settlement
    Server->>Facilitator: POST /settle<br/>{signature, from, to, amount, nonce}
    
    Note over Facilitator: Facilitator verifies signature
    Facilitator->>Solana: Transfer using Permanent Delegate<br/>(No user signature needed!)
    Solana-->>Facilitator: Transfer complete
    
    Facilitator-->>Server: Settlement confirmed + tx_hash
    Server-->>Client: 200 OK + Article content<br/>X-PAYMENT-RESPONSE: {tx_hash}
Loading

Knowledge Requirements by Participant

Users (Minimal Blockchain Knowledge)

Required Knowledge:
✓ How to sign messages with wallet
✓ How to hold xUSDC tokens
✗ NO knowledge of Solana transactions
✗ NO knowledge of gas/fees
✗ NO knowledge of PDAs, programs, etc.

User Experience:
1. One-time: "Deposit USDC to get xUSDC" (like depositing to PayPal)
2. Ongoing: Click "Pay" → Sign message → Done

Resource Servers (Zero Blockchain Knowledge)

Required Knowledge:
✓ How to generate payment requirements (amount, wallet address)
✓ How to call facilitator API
✗ NO Solana transaction knowledge
✗ NO program interaction
✗ NO key management beyond having an address

Server Experience:
1. Return 402 with payment requirements
2. Receive payment header
3. Call facilitator.settle(payment_data)
4. Serve content when confirmed

Facilitator (Full Solana Knowledge)

Required Knowledge:
✓ Solana transaction construction
✓ Program interactions
✓ Permanent delegate usage
✓ Ed25519 signature verification
✓ RPC node management
✓ Error handling & retries

Facilitator Responsibilities:
1. Verify signatures match payment intent
2. Construct & submit Solana transactions
3. Handle all blockchain complexity
4. Provide simple API to servers

Why This Approach vs. Full Transaction Signing

Your Boss's Approach (Full Transaction Signing)

User Flow:
1. Server creates Solana transaction
2. User reviews complex transaction
3. User signs full transaction
4. Facilitator adds fee payer signature
5. Submit to Solana

Problems:
❌ Users see scary Solana transaction details
❌ Servers must understand Solana transactions
❌ Security risk - users signing arbitrary txs
❌ Cloudflare/Vercel won't implement this
❌ Transaction can be modified after signing
❌ Complex error handling

Permanent Delegate Approach

User Flow:
1. User signs simple message: "Pay X to Y"
2. Done.

Advantages:
✅ Users sign human-readable messages
✅ Servers need zero blockchain knowledge
✅ Cloudflare/Vercel can implement easily
✅ Same UX as EIP-3009 on Ethereum
✅ Signature intent is immutable
✅ Facilitator handles all complexity

Security Comparison

Message Signing (w Permanent Delegate)

// User signs exactly this:
{
  "action": "pay",
  "from": "user_wallet",
  "to": "server_wallet",
  "amount": "0.001",
  "asset": "xUSDC",
  "nonce": "abc123",
  "validUntil": 1234567890
}

// Intent is crystal clear
// Cannot be modified
// Easy to audit

Transaction Signing Approach

// User signs complex transaction:
{
  "instructions": [{
    "programId": "TokenkegQ...",
    "keys": [
      {"pubkey": "...", "isSigner": true, "isWritable": true},
      {"pubkey": "...", "isSigner": false, "isWritable": true},
      // ... 8 more accounts
    ],
    "data": "0x0a0b0c0d..." // Opaque bytes
  }],
  "recentBlockhash": "...",
  "signatures": [...]
}

// What is this doing?
// Can instructions be added?
// Hard to audit

Implementation Simplicity

For Platform Providers (Cloudflare, Vercel, etc.)

With Message Signing:

// Simple to implement
function handlePayment(paymentHeader) {
  const { signature, params } = decode(paymentHeader);
  
  // Just forward to facilitator
  return fetch('facilitator.com/settle', {
    method: 'POST',
    body: JSON.stringify({ signature, ...params })
  });
}

With Transaction Signing:

// Complex and risky
function handlePayment(signedTx) {
  // Must understand Solana transactions
  // Must validate transaction safety
  // Must handle partial signatures
  // Must deal with transaction malleability
  // Platform providers won't do this!
}

Summary

The Permanent Delegate approach maintains the elegant simplicity of x402:

  • Users: "I authorize payment of X"
  • Servers: "I need payment of X"
  • Facilitator: "I'll handle the blockchain stuff"

This separation of concerns is why x402 works so well on EVM, and why the permanent delegate approach is the right path for Solana.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment