Comparing glam with x402

Author

@0xinit

Stars

53

Repository

0xinit/cryptoskills

skills/glam/SKILL.md

GLAM Protocol Skill

GLAM provides programmable investment infrastructure on Solana: vaults with access control, DeFi integrations, and tokenization.

Quick Start

# Install CLI
npm install -g @glamsystems/glam-cli

# Configure (~/.config/glam/config.json)
cat > ~/.config/glam/config.json << 'EOF'
{
  "keypair_path": "~/.config/solana/id.json",
  "json_rpc_url": "https://api.mainnet-beta.solana.com"
}
EOF

# Create vault, set active, enable integrations, verify
glam-cli vault create ./vault-template.json
glam-cli vault set <VAULT_STATE_PUBKEY>
glam-cli integration enable JupiterSwap KaminoLend
glam-cli vault view

Critical: Integration Enablement

You MUST enable integrations BEFORE using them. This is the most common error.

Available: JupiterSwap, DriftProtocol, KaminoLend, KaminoVaults, KaminoFarms, DriftVaults, SplToken, CCTP, GlamMint, Marinade (staging), StakePool (staging), SanctumSingle (staging), SanctumMulti (staging), StakeProgram (staging).

Staging integrations require --bypass-warning.


Workflows

Tokenized Vault Setup

glam-cli vault create ./tokenized-vault-template.json
glam-cli vault set <VAULT_STATE_PUBKEY>
glam-cli integration enable JupiterSwap DriftProtocol KaminoLend
glam-cli manage price                          # Set initial NAV price
glam-cli jupiter set-max-slippage 100          # Configure swap policy

# Optional: delegate trading permissions (protocol-scoped)
glam-cli delegate grant <TRADER_PUBKEY> SwapAny --protocol JupiterSwap
glam-cli delegate grant <TRADER_PUBKEY> Deposit Withdraw CreateModifyOrders CancelOrders --protocol DriftProtocol

# Optional: set timelock (24 hours)
glam-cli timelock set 86400

Drift Trading

glam-cli integration enable DriftProtocol
glam-cli drift-protocol init-user              # Required once
glam-cli drift-protocol deposit 0 1000         # Deposit USDC collateral
glam-cli drift-protocol perp long 0 1 0        # Open position

Kamino Lending

glam-cli integration enable KaminoLend
glam-cli kamino-lend init                      # Required once
glam-cli kamino-lend deposit \
  7u3HeHxYDLhnCoErrtycNokbQYbWGzLs6JSDqGAv5PfF \
  EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v \
  1000

Decision Tree

GoalIntegrationCommand
Swap tokensJupiterSwapjupiter swap
Lend for yieldKaminoLendkamino-lend deposit
Stake SOL (liquid)Marinade (staging)marinade --bypass-warning stake
Stake SOL (LST)StakePool / SanctumSingle / SanctumMulti (staging)lst --bypass-warning stake <pool> <amount>
Stake SOL (native)StakeProgram (staging)stake --bypass-warning list / deactivate / withdraw
Kamino vaultsKaminoVaultskamino-vaults deposit
Drift vaultsDriftVaultsdrift-vaults deposit
Trade perpsDriftProtocoldrift-protocol init-userdepositperp
Trade spotDriftProtocoldrift-protocol init-userdepositspot
Tokenized vaultvault createmanage price → investors invest subscribe
Manage share tokensSDK only: client.mint.* (freeze, issue, burn, forceTransfer)
Bridge USDCCCTPcctp bridge-usdc <amount> <domain> <dest> (0=ETH, 1=AVAX, 2=OP, 3=ARB, 6=BASE, 7=POLYGON)
Timelocktimelock set <seconds>

Common Errors

ErrorSolution
"Signer is not authorized"Check vault view for owner; grant delegate if needed
"Integration not enabled"integration enable <NAME>
"Asset not in allowlist"vault allowlist-asset <MINT>
"User not initialized"drift-protocol init-user or kamino-lend init
"No route found"Try smaller amount; check token liquidity
"Slippage exceeded"Increase --slippage-bps or reduce amount
"Insufficient collateral"drift-protocol deposit more
"Account is frozen"SDK: client.mint.setTokenAccountsStates()
"Missing jupiter_api_key"Add jupiter_api_key to config.json

See troubleshooting for detailed solutions.


Common Mints

TokenAddress
SOLSo11111111111111111111111111111111111111112
USDCEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v
USDTEs9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB
mSOLmSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So
jitoSOLJ1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn

SDK Quick Start

import {
  GlamClient,
  WSOL,
  USDC,
  getProgramAndBitflagByProtocolName,
} from "@glamsystems/glam-sdk";
import { BN } from "@coral-xyz/anchor";

const client = new GlamClient({ wallet });

// Create vault
const { vaultPda } = await client.vault.create({
  name: "My Vault",
  assets: [WSOL, USDC],
});

// Enable Jupiter integration
const perms = getProgramAndBitflagByProtocolName();
const [program, bitflag] = perms["JupiterSwap"];
await client.access.enableProtocols(vaultPda, program, parseInt(bitflag, 2));

// Swap
await client.jupiterSwap.swap(vaultPda, {
  inputMint: USDC,
  outputMint: WSOL,
  amount: new BN(100_000_000),
  slippageBps: 50,
});

Reference

Author

@0xinit

Stars

53

Repository

0xinit/cryptoskills

skills/x402/SKILL.md

x402

x402 is an open payment protocol that revives the HTTP 402 "Payment Required" status code to enable instant stablecoin payments directly over HTTP. It uses a three-actor model — Client (buyer), Resource Server (seller), and Facilitator (settlement) — to let any HTTP endpoint accept payment without API keys, sessions, or accounts. The protocol is built on EIP-3009 transferWithAuthorization for gasless USDC transfers, meaning clients sign an off-chain authorization and the facilitator broadcasts the on-chain settlement.

Package ecosystem:

  • Server (TypeScript): @x402/express, @x402/hono, @x402/next
  • Client (TypeScript): @x402/fetch, @x402/axios
  • Client (Python): pip install x402
  • Client (Go): go get github.com/coinbase/x402/go
  • Core: @x402/core, @x402/evm, @x402/svm

CDP Facilitator endpoint: https://api.cdp.coinbase.com/platform/v2/x402

What You Probably Got Wrong

LLMs have stale training data. These are the most common mistakes.

  • "x402 requires API keys or OAuth" -- There are no API keys. Payment IS authentication. The client signs a USDC transfer authorization, includes it in the X-PAYMENT header, and the server verifies it. If the signature and balance check pass, the request is authorized. No accounts, no sessions, no bearer tokens.

  • "x402 only works on Base" -- The protocol supports any EVM chain where USDC implements EIP-3009 (Base, Ethereum, Arbitrum, Optimism, Polygon) and Solana. Network identifiers use CAIP-2 format: eip155:8453 for Base mainnet, eip155:1 for Ethereum, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp for Solana mainnet.

  • "Nonces are sequential counters" -- x402 nonces are random 32-byte values (bytes32), not incrementing integers. Each authorization generates a fresh random nonce. The ERC-3009 contract tracks used nonces on-chain to prevent replay. Using sequential nonces or reusing a nonce will cause settlement to revert.

  • "The client pays gas" -- The client never submits an on-chain transaction. The client signs an off-chain EIP-712 authorization. The facilitator broadcasts the transferWithAuthorization call and pays gas. CDP's hosted facilitator absorbs gas costs on Base mainnet for free-tier usage (1,000 tx/month).

  • "x402 supports any ERC-20 token" -- The primary path uses USDC via EIP-3009 transferWithAuthorization for truly gasless transfers. A Permit2 fallback exists for arbitrary ERC-20 tokens, but it requires the user to have previously approved the Permit2 contract, which needs native gas. For the simplest integration, use USDC.

  • "The old package name is @coinbase/x402" -- The current packages use the @x402/* namespace (e.g., @x402/express, @x402/fetch). Earlier versions used x402-express and x402-fetch without the scope. The @coinbase/x402 name was never published.

  • "The payment header is called PAYMENT-SIGNATURE" -- The header name is X-PAYMENT. It contains a base64-encoded JSON payload with the authorization, signature, scheme, and network. The server responds with X-PAYMENT-RESPONSE.

Core Concepts

Three-Actor Model

┌──────────┐      1. Request resource       ┌─────────────────┐
│          │ ─────────────────────────────► │                 │
│          │      2. HTTP 402 + payment     │   Resource      │
│  Client  │ ◄───────────────────────────── │   Server        │
│ (Buyer)  │      3. Request + X-PAYMENT    │   (Seller)      │
│          │ ─────────────────────────────► │                 │
│          │      6. HTTP 200 + resource    │                 │
│          │ ◄───────────────────────────── │                 │
└──────────┘                                └────────┬────────┘
                                              4. Verify │ 5. Settle
                                            ┌────────▼────────┐
                                            │                 │
                                            │  Facilitator    │
                                            │  (Settlement)   │
                                            │                 │
                                            └─────────────────┘

Payment Flow Lifecycle

  1. Client sends a standard HTTP request to a protected endpoint
  2. Resource Server responds with HTTP 402 and payment requirements in the response body (scheme, network, amount, asset, payTo address)
  3. Client constructs an EIP-3009 authorization (from, to, value, validAfter, validBefore, nonce), signs it with EIP-712, and retries the request with the X-PAYMENT header containing the base64-encoded payload
  4. Resource Server forwards the payment to the Facilitator for verification (/verify endpoint)
  5. Facilitator validates the signature, checks balance, and simulates the transfer
  6. Resource Server serves the resource
  7. Resource Server requests settlement from the Facilitator (/settle endpoint)
  8. Facilitator calls transferWithAuthorization on the USDC contract to move funds on-chain

HTTP 402 Response Format

When a client hits a paid endpoint without a payment header, the server returns:

HTTP/1.1 402 Payment Required
Content-Type: application/json

{
  "x402Version": 2,
  "accepts": [
    {
      "scheme": "exact",
      "network": "eip155:8453",
      "amount": "10000",
      "asset": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
      "payTo": "0xSELLER_ADDRESS",
      "maxTimeoutSeconds": 60,
      "extra": {
        "assetTransferMethod": "eip3009",
        "name": "USDC",
        "version": "2"
      }
    }
  ],
  "error": "X-PAYMENT header is required"
}

The amount is in the token's smallest unit. For USDC (6 decimals), 10000 = $0.01.

ERC-3009 transferWithAuthorization

EIP-3009 enables gasless token transfers by separating authorization from execution. The token holder signs an off-chain message; anyone can submit the signed authorization to the contract.

Function Signature

function transferWithAuthorization(
    address from,
    address to,
    uint256 value,
    uint256 validAfter,
    uint256 validBefore,
    bytes32 nonce,
    bytes   signature
) external;

EIP-712 Domain

EIP712Domain({
    name: "USDC",
    version: "2",
    chainId: <chain_id>,
    verifyingContract: <usdc_address>
})

Authorization Struct (for signing)

TransferWithAuthorization({
    from: address,
    to: address,
    value: uint256,
    validAfter: uint256,
    validBefore: uint256,
    nonce: bytes32
})

Key properties:

  • nonce is a random bytes32, not a sequential counter. The contract maintains a mapping of (authorizer, nonce) => bool to track used nonces.
  • validAfter and validBefore define a time window. Set validAfter to 0 (or current timestamp) and validBefore to current timestamp + maxTimeoutSeconds.
  • The facilitator calls this function — the signer never needs native gas.
  • USDC on Base, Ethereum, Arbitrum, Optimism, and Polygon all implement EIP-3009.

Server Setup (TypeScript)

Express Middleware

npm install @x402/core @x402/evm @x402/express
import express from "express";
import { paymentMiddleware, x402ResourceServer } from "@x402/express";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { HTTPFacilitatorClient } from "@x402/core/server";

const app = express();

const facilitatorClient = new HTTPFacilitatorClient({
  url: "https://api.cdp.coinbase.com/platform/v2/x402",
});

const resourceServer = new x402ResourceServer(facilitatorClient)
  .register("eip155:8453", new ExactEvmScheme());

app.use(
  paymentMiddleware(
    {
      "GET /api/weather": {
        accepts: {
          scheme: "exact",
          network: "eip155:8453",
          price: "$0.01",
          payTo: process.env.PAYMENT_ADDRESS as `0x${string}`,
        },
        description: "Current weather data",
      },
      "POST /api/analyze": {
        accepts: {
          scheme: "exact",
          network: "eip155:8453",
          price: "$0.05",
          payTo: process.env.PAYMENT_ADDRESS as `0x${string}`,
        },
        description: "AI text analysis",
      },
    },
    resourceServer,
  ),
);

app.get("/api/weather", (_req, res) => {
  res.json({ temperature: 72, unit: "F", location: "San Francisco" });
});

app.post("/api/analyze", (req, res) => {
  res.json({ sentiment: "positive", confidence: 0.92 });
});

app.listen(3000, () => {
  console.log("x402 server running on port 3000");
});

Hono and Next.js middleware follow the same pattern — install @x402/hono or @x402/next and use the same paymentMiddleware + x402ResourceServer setup with framework-specific imports.

Client Setup (TypeScript)

Using @x402/fetch (Recommended)

The wrapFetchWithPaymentFromConfig function wraps the native fetch API to automatically handle 402 responses — detect the payment requirement, sign the authorization, and retry with the X-PAYMENT header.

npm install @x402/core @x402/evm @x402/fetch viem
import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import type { Hex } from "viem";

const account = privateKeyToAccount(process.env.PRIVATE_KEY as Hex);

const fetchWithPayment = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [
    {
      network: "eip155:8453",
      client: new ExactEvmScheme(account),
    },
  ],
});

const response = await fetchWithPayment("https://api.example.com/api/weather");
if (!response.ok) {
  throw new Error(`Request failed: ${response.status}`);
}

const data = await response.json();
console.log(data);

Multi-Network Client

import { x402Client, wrapFetchWithPayment } from "@x402/fetch";
import { registerExactEvmScheme } from "@x402/evm/exact/client";
import { registerExactSvmScheme } from "@x402/svm/exact/client";
import { privateKeyToAccount } from "viem/accounts";
import { Keypair } from "@solana/web3.js";
import type { Hex } from "viem";

const evmAccount = privateKeyToAccount(process.env.EVM_PRIVATE_KEY as Hex);
const solanaKeypair = Keypair.fromSecretKey(
  Buffer.from(process.env.SOLANA_PRIVATE_KEY as string, "base64")
);

const client = new x402Client();
registerExactEvmScheme(client, { signer: evmAccount });
registerExactSvmScheme(client, { signer: solanaKeypair });

const fetchWithPayment = wrapFetchWithPayment(fetch, client);

// Automatically handles both EVM and Solana payment requirements
const evmResponse = await fetchWithPayment("https://api.example.com/evm-data");
const solResponse = await fetchWithPayment("https://api.example.com/sol-data");

For manual header construction without the wrapper, see the agent-client and python-client examples in this skill's examples/ directory.

Client Setup (Python)

pip install "x402[evm,requests]"

Using the x402 Client

from x402 import x402ClientSync
from x402.evm import ExactEvmScheme
from eth_account import Account

wallet = Account.from_key("0xYOUR_PRIVATE_KEY")

client = x402ClientSync()
client.register("eip155:*", ExactEvmScheme(signer=wallet))

# Make a paid request — the client handles the 402 flow automatically
import requests

session = requests.Session()
response = session.get("https://api.example.com/api/weather")

if response.status_code == 402:
    payment_required = response.json()
    payload = client.create_payment_payload(payment_required)
    response = session.get(
        "https://api.example.com/api/weather",
        headers={"X-PAYMENT": payload.to_header()},
    )

print(response.json())

For manual EIP-712 signing without the x402 package, see the python-client example in this skill's examples/ directory.

Facilitator Patterns

CDP Hosted Facilitator (Default)

Coinbase Developer Platform provides a hosted facilitator with two endpoints:

EndpointMethodPurpose
/verifyPOSTValidate signature, check balance, simulate transfer
/settlePOSTExecute transferWithAuthorization on-chain

Pricing:

  • Free tier: 1,000 transactions/month
  • Standard: $0.001 per transaction thereafter
  • Base mainnet: Zero gas fees (gas absorbed by facilitator)
import { HTTPFacilitatorClient } from "@x402/core/server";

const facilitator = new HTTPFacilitatorClient({
  url: "https://api.cdp.coinbase.com/platform/v2/x402",
});

Custom Facilitator

Run your own facilitator for full control over settlement:

import { createFacilitatorServer } from "@x402/core/facilitator";
import { ExactEvmScheme } from "@x402/evm/exact/server";
import { createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";
import { base } from "viem/chains";
import type { Hex } from "viem";

// Facilitator wallet pays gas for settlement
const facilitatorAccount = privateKeyToAccount(
  process.env.FACILITATOR_PRIVATE_KEY as Hex
);

const walletClient = createWalletClient({
  account: facilitatorAccount,
  chain: base,
  transport: http(),
});

const server = createFacilitatorServer({
  schemes: {
    "eip155:8453": new ExactEvmScheme({ walletClient }),
  },
});

server.listen(4000, () => {
  console.log("Custom facilitator on port 4000");
});

Settlement Flow

  1. Resource server sends the payment payload to facilitator /verify
  2. Facilitator checks: signature validity, signer balance, authorization parameters, transfer simulation
  3. If valid, resource server serves the response and calls facilitator /settle
  4. Facilitator submits transferWithAuthorization(from, to, value, validAfter, validBefore, nonce, signature) to the USDC contract
  5. Facilitator returns the transaction hash and receipt

The facilitator cannot modify the amount or destination. It serves only as the transaction broadcaster.

Agent-to-Agent Payments

x402 enables autonomous agent commerce where AI agents discover services, negotiate prices, and pay without human intervention.

Discovery Pattern

// Agent discovers paid endpoints via standard HTTP
const discoveryResponse = await fetch("https://agent-service.com/.well-known/x402");
const services = await discoveryResponse.json();

// services contains available endpoints with pricing
// {
//   "endpoints": [
//     { "path": "/api/summarize", "price": "$0.02", "network": "eip155:8453" },
//     { "path": "/api/translate", "price": "$0.01", "network": "eip155:8453" }
//   ]
// }

Autonomous Payment Execution

import { wrapFetchWithPaymentFromConfig } from "@x402/fetch";
import { ExactEvmScheme } from "@x402/evm";
import { privateKeyToAccount } from "viem/accounts";
import type { Hex } from "viem";

const agentAccount = privateKeyToAccount(process.env.AGENT_PRIVATE_KEY as Hex);

const agentFetch = wrapFetchWithPaymentFromConfig(fetch, {
  schemes: [
    { network: "eip155:8453", client: new ExactEvmScheme(agentAccount) },
  ],
});

// Agent autonomously pays for another agent's service
async function callPaidService(url: string, body: Record<string, unknown>): Promise<unknown> {
  const response = await agentFetch(url, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(body),
  });

  if (!response.ok) {
    throw new Error(`Service call failed: ${response.status}`);
  }

  return response.json();
}

// Multi-step agent workflow: summarize -> translate -> store
const summary = await callPaidService("https://summarizer.agent/api/summarize", {
  text: "Long document content...",
});

const translation = await callPaidService("https://translator.agent/api/translate", {
  text: summary,
  targetLanguage: "es",
});

Pricing Strategies

Per-Request (Fixed Price)

Charge a flat fee per API call. Simplest model.

"GET /api/weather": {
  accepts: {
    scheme: "exact",
    network: "eip155:8453",
    price: "$0.01",
    payTo: process.env.PAYMENT_ADDRESS as `0x${string}`,
  },
  description: "Weather data — $0.01 per request",
},

Tiered Pricing

Different prices per endpoint based on compute cost.

const routes = {
  "GET /api/basic-query": {
    accepts: {
      scheme: "exact",
      network: "eip155:8453",
      price: "$0.001",
      payTo: paymentAddress,
    },
    description: "Basic database query",
  },
  "POST /api/ai-analysis": {
    accepts: {
      scheme: "exact",
      network: "eip155:8453",
      price: "$0.05",
      payTo: paymentAddress,
    },
    description: "AI-powered analysis — higher compute cost",
  },
  "POST /api/image-generation": {
    accepts: {
      scheme: "exact",
      network: "eip155:8453",
      price: "$0.10",
      payTo: paymentAddress,
    },
    description: "Image generation — GPU cost",
  },
};

Dynamic Pricing

Adjust prices based on demand, time of day, or resource utilization.

function getDynamicPrice(basePrice: number, currentLoad: number): string {
  // Surge pricing: 1x at 0% load, up to 3x at 100% load
  const multiplier = 1 + (currentLoad / 100) * 2;
  const price = basePrice * multiplier;
  return `$${price.toFixed(4)}`;
}

Replay Protection

x402 uses two mechanisms to prevent payment replay:

Random Nonces

Every authorization includes a random 32-byte nonce. The USDC contract's authorizationState mapping tracks whether a (from, nonce) pair has been used. Attempting to settle with a used nonce reverts with authorization is used or canceled.

import { keccak256, encodePacked, toHex } from "viem";

// Generate a unique nonce per authorization
function generateNonce(): Hex {
  const randomBytes = crypto.getRandomValues(new Uint8Array(32));
  return toHex(randomBytes);
}

Expiration Timestamps

The validBefore field ensures authorizations cannot be settled after their time window. Best practice: set validBefore to now + maxTimeoutSeconds (typically 30-60 seconds).

const now = Math.floor(Date.now() / 1000);
const authorization = {
  validAfter: BigInt(0),
  validBefore: BigInt(now + 60), // 60-second window
  nonce: generateNonce(),
  // ... other fields
};

If a facilitator does not settle within the validBefore window, the authorization expires and the client's funds are never moved. The client can safely retry with a new authorization.

Permit2 Fallback

For ERC-20 tokens that do not implement EIP-3009, x402 supports a Permit2-based fallback.

How It Differs

FeatureEIP-3009Permit2
Gasless setupYes (native)Requires one-time approval tx
Token supportUSDC onlyAny ERC-20
ContractToken contract itselfUniswap Permit2 contract
ProxyNonex402ExactPermit2Proxy at 0x4020CD856C882D5fb903D99CE35316A085Bb0001

The Permit2 path requires the user to have approved the Permit2 contract (0x000000000022D473030F116dDEE9F6B43aC78BA3). If the allowance is missing, the server returns HTTP 412 with error code PERMIT2_ALLOWANCE_REQUIRED.

Supported Networks

NetworkCAIP-2 IdentifierUSDC AddressStatus
Baseeip155:84530x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913Primary
Base Sepoliaeip155:845320x036CbD53842c5426634e7929541eC2318f3dCF7eTestnet
Ethereumeip155:10xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48Supported
Arbitrumeip155:421610xaf88d065e77c8cC2239327C5EDb3A432268e5831Supported
Optimismeip155:100x0b2C639c533813f4Aa9D7837CAf62653d097Ff85Supported
Polygoneip155:1370x3c499c542cEF5E3811e1192ce70d8cC03d5c3359Supported
Solanasolana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdpEPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1vSupported

Last verified: February 2026

References

AI Skill Finder

Ask me what skills you need

What are you building?

Tell me what you're working on and I'll find the best agent skills for you.