Breeze x402
Scanned@keeganthomp
npx machina-cli add skill @keeganthomp/breeze-x402-payment-api --openclawBreeze x402 Payment API
Interact with Breeze through its x402 payment-gated HTTP API. Each protected request pays a small USDC micropayment, then returns API data or an unsigned Solana transaction.
Quick Start: Minimum Viable Flow
This is the fastest path from zero to a working deposit. Read this before anything else.
What you need:
- A Solana wallet funded with USDC and ~0.01 SOL (for transaction fees)
- Node.js installed
Step 0: Generate a wallet (skip if you already have one)
Run once to create and save a keypair:
// generate-wallet.js
const { Keypair } = require('@solana/web3.js');
const fs = require('fs');
// Install first: npm install @solana/web3.js --legacy-peer-deps
const bs58Module = require('bs58');
const bs58 = bs58Module.default || bs58Module;
const keypair = Keypair.generate();
const secretKeyBase58 = bs58.encode(keypair.secretKey);
console.log('Public key (fund this address):', keypair.publicKey.toBase58());
// Save secret key to file — never print it to the console
fs.writeFileSync('wallet-backup.json', JSON.stringify(Array.from(keypair.secretKey)));
fs.writeFileSync('.env', `WALLET_PRIVATE_KEY=${secretKeyBase58}\n`);
console.log('Saved .env with WALLET_PRIVATE_KEY and wallet-backup.json — keep both secret and out of git!');
node generate-wallet.js
Fund the printed public key with USDC and at least 0.01 SOL before continuing. The .env file now contains your WALLET_PRIVATE_KEY.
Step 1: Preflight checks
Verify everything is reachable before writing code:
# x402 server health
curl https://x402.breeze.baby/healthz
# Breeze strategy info (no auth needed)
curl https://api.breeze.baby/strategy-info/43620ba3-354c-456b-aa3c-5bf7fa46a6d4
# Wallet USDC balance (replace YOUR_WALLET_ADDRESS)
curl https://api.mainnet-beta.solana.com \
-X POST -H "Content-Type: application/json" \
-d '{"jsonrpc":"2.0","id":1,"method":"getTokenAccountsByOwner","params":["YOUR_WALLET_ADDRESS",{"mint":"EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"},{"encoding":"jsonParsed"}]}'
Expected: healthz returns {"status":"ok"}, strategy-info returns strategy data, token query shows your USDC balance. If any of these fail, fix the connectivity issue before proceeding.
Step 2: Install
npm install @faremeter/fetch @faremeter/payment-solana @faremeter/wallet-solana @faremeter/info @solana/web3.js bs58 --legacy-peer-deps
--legacy-peer-depsis required for npm. Without it,@faremeter/*peer dependency conflicts cause npm to silently remove@solana/web3.js.
Step 3: Run the deposit script
Save as deposit.js and run with node deposit.js:
// deposit.js — CommonJS, no TypeScript needed
'use strict';
const { wrap } = require('@faremeter/fetch');
const { createPaymentHandler } = require('@faremeter/payment-solana/exact');
const { createLocalWallet } = require('@faremeter/wallet-solana');
const { Connection, Keypair, PublicKey, VersionedTransaction, Transaction } = require('@solana/web3.js');
// bs58 exports .default in some CJS environments
const bs58Module = require('bs58');
const bs58 = bs58Module.default || bs58Module;
async function main() {
const WALLET_PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY;
if (!WALLET_PRIVATE_KEY) throw new Error('Set WALLET_PRIVATE_KEY env var');
const API_URL = 'https://x402.breeze.baby';
const STRATEGY_ID = '43620ba3-354c-456b-aa3c-5bf7fa46a6d4';
const USDC_MINT = 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v';
const DEPOSIT_AMOUNT = 1_000_000; // 1 USDC (6 decimals)
// Setup
const keypair = Keypair.fromSecretKey(bs58.decode(WALLET_PRIVATE_KEY));
const connection = new Connection('https://api.mainnet-beta.solana.com');
const wallet = await createLocalWallet('mainnet-beta', keypair);
const paymentHandler = createPaymentHandler(wallet, new PublicKey(USDC_MINT), connection);
const fetchWithPayment = wrap(fetch, { handlers: [paymentHandler] });
console.log('Wallet:', keypair.publicKey.toBase58());
// Build deposit transaction
const res = await fetchWithPayment(`${API_URL}/deposit`, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify({
amount: DEPOSIT_AMOUNT,
user_key: keypair.publicKey.toBase58(),
strategy_id: STRATEGY_ID,
base_asset: USDC_MINT,
}),
});
if (!res.ok) {
const text = await res.text();
throw new Error(`Deposit failed (${res.status}): ${text}`);
}
// Parse transaction string (may be bare or JSON-wrapped)
const raw = (await res.text()).trim();
const txString = raw.startsWith('"') ? JSON.parse(raw) : raw;
// Sign and send (try versioned tx first, fall back to legacy)
const bytes = Buffer.from(txString, 'base64');
let sig;
try {
const tx = VersionedTransaction.deserialize(bytes);
tx.sign([keypair]);
sig = await connection.sendRawTransaction(tx.serialize());
} catch {
const tx = Transaction.from(bytes);
tx.partialSign(keypair);
sig = await connection.sendRawTransaction(tx.serialize());
}
await connection.confirmTransaction(sig, 'confirmed');
console.log('Done! View transaction:', `https://solscan.io/tx/${sig}`);
}
main().catch(console.error);
# Set WALLET_PRIVATE_KEY from the .env created in Step 0
export $(cat .env | xargs) && node deposit.js
Endpoint Paths: x402 API vs Direct REST API
These are different APIs with different paths. Do not mix them up.
| API | Base URL | Endpoints | Auth method |
|---|---|---|---|
| x402 (this skill) | https://x402.breeze.baby | /balance/:fund_user<br>/deposit<br>/withdraw<br>/healthz (free) | USDC micropayment via x402 protocol |
| Direct REST API | https://api.breeze.baby | /deposit/tx<br>/withdraw/tx<br>/strategy-info/:id | x-api-key header |
This skill uses the x402 API. The direct REST API uses different paths and API key auth — do not mix them.
When to use this skill
- "check my Breeze balance" or "show positions/yield"
- "deposit X token into Breeze strategy"
- "withdraw X token from Breeze strategy"
- "sign and send the transaction built by the API"
Required inputs
WALLET_PRIVATE_KEY(base58 secret key — see Step 0 to generate one)- Optional
STRATEGY_ID(defaults to43620ba3-354c-456b-aa3c-5bf7fa46a6d4— you can use any Breeze strategy ID) - Optional
X402_API_URL(defaulthttps://x402.breeze.baby) - Optional
SOLANA_RPC_URL(defaulthttps://api.mainnet-beta.solana.com) - Optional
BASE_ASSETmint (default USDC mint)
Security rules
- Never print or echo
WALLET_PRIVATE_KEY. - Never return raw secret values in tool output.
- If a command fails, redact secrets before showing logs.
- Add
wallet-backup.jsonto.gitignoreimmediately.
Dependencies and install
# npm (--legacy-peer-deps required)
npm install @faremeter/fetch @faremeter/payment-solana @faremeter/wallet-solana @faremeter/info @solana/web3.js bs58 --legacy-peer-deps
# pnpm
pnpm add @faremeter/fetch @faremeter/payment-solana @faremeter/wallet-solana @faremeter/info @solana/web3.js bs58
# bun
bun add @faremeter/fetch @faremeter/payment-solana @faremeter/wallet-solana @faremeter/info @solana/web3.js bs58
Setup: Payment-Wrapped Fetch (TypeScript)
Use this setup once per runtime. It automatically handles x402 challenges (402 → build payment proof → retry request):
import { wrap } from "@faremeter/fetch";
import { createPaymentHandler } from "@faremeter/payment-solana/exact";
import { createLocalWallet } from "@faremeter/wallet-solana";
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import bs58 from "bs58";
const API_URL = (process.env.X402_API_URL ?? "https://x402.breeze.baby").replace(/\/$/, "");
const SOLANA_RPC_URL = process.env.SOLANA_RPC_URL ?? "https://api.mainnet-beta.solana.com";
const STRATEGY_ID = process.env.STRATEGY_ID || "43620ba3-354c-456b-aa3c-5bf7fa46a6d4";
const BASE_ASSET = process.env.BASE_ASSET ?? "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
const WALLET_PRIVATE_KEY = process.env.WALLET_PRIVATE_KEY!;
const keypair = Keypair.fromSecretKey(bs58.decode(WALLET_PRIVATE_KEY));
const connection = new Connection(SOLANA_RPC_URL);
const wallet = await createLocalWallet("mainnet-beta", keypair);
const walletPublicKey = keypair.publicKey.toBase58();
const USDC_MINT = new PublicKey(BASE_ASSET);
const paymentHandler = createPaymentHandler(wallet, USDC_MINT, connection);
const fetchWithPayment = wrap(fetch, { handlers: [paymentHandler] });
API endpoint contract
Check Balance
GET /balance/:fund_user
Returns JSON with positions, deposited amounts, yield earned, and APY. Values are in base units. Convert to human amounts with token decimals.
const response = await fetchWithPayment(
`${API_URL}/balance/${encodeURIComponent(walletPublicKey)}`,
{ method: "GET" },
);
const balances = await response.json();
Deposit
POST /deposit
Content-Type: application/json
Builds an unsigned deposit transaction.
amount must be base units (example: 10_000_000 = 10 USDC).
const response = await fetchWithPayment(`${API_URL}/deposit`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
amount: 10_000_000, // 10 USDC (6 decimals)
user_key: walletPublicKey,
strategy_id: STRATEGY_ID,
base_asset: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
}),
});
const txString = await response.text(); // encoded unsigned transaction
Withdraw
POST /withdraw
Content-Type: application/json
Builds an unsigned withdrawal transaction. Supports optional WSOL handling flags.
amount must be base units.
const response = await fetchWithPayment(`${API_URL}/withdraw`, {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({
amount: 5_000_000,
user_key: walletPublicKey,
strategy_id: STRATEGY_ID,
base_asset: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
all: false,
exclude_fees: true, // always recommended
// For wrapped SOL withdrawals only:
// unwrap_wsol_ata: true, // unwrap WSOL to native SOL
// create_wsol_ata: true, // create WSOL ATA if needed
// detect_wsol_ata: true, // auto-detect WSOL ATA existence
}),
});
const txString = await response.text();
Withdraw parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
amount | number | yes | Amount in base units |
user_key | string | yes | User's Solana public key |
strategy_id | string | yes | Breeze strategy ID (default: 43620ba3-354c-456b-aa3c-5bf7fa46a6d4, or any valid strategy) |
base_asset | string | yes | Token mint address |
all | boolean | no | Withdraw entire position |
exclude_fees | boolean | no | Exclude fees from amount (recommended: true) |
unwrap_wsol_ata | boolean | no | Unwrap WSOL to native SOL after withdraw |
create_wsol_ata | boolean | no | Create WSOL ATA if it doesn't exist |
detect_wsol_ata | boolean | no | Auto-detect WSOL ATA and set flags accordingly |
WSOL handling: when withdrawing WSOL (So11111111111111111111111111111111111111112), pass unwrap_wsol_ata: true to receive native SOL.
Health Check
GET /healthz
Free endpoint (no x402 payment required). Returns {"status":"ok"} when the service is running. Use this to verify connectivity before making paid requests.
Workflow checklists
Copy a checklist into your working notes and mark each step complete.
Balance workflow
Task Progress:
- Read
wallet public keyinput - Call
GET /balance/:fund_userwith URL-encoded wallet key - Verify
response.ok; if not, capture status/body and stop - Parse JSON response
- Convert base units to human-readable values using token decimals
- Return balances, yield, and APY clearly
Deposit workflow
Task Progress:
- Confirm token mint and decimals
- Convert user amount to base units (
floor(amount * 10^decimals)) - Call
POST /depositwith validated payload - Verify
response.ok; if not, capture status/body and stop - Extract transaction string from response text
- Sign and broadcast transaction on Solana
- Confirm transaction and return explorer link
Withdraw workflow
Task Progress:
- Confirm token mint and decimals
- Convert user amount to base units unless
all=true - Set
exclude_fees: trueunless user asks otherwise - For WSOL + native SOL output, set
unwrap_wsol_ata: true - Call
POST /withdrawwith validated payload - Verify
response.ok; if not, capture status/body and stop - Extract transaction string from response text
- Sign and broadcast transaction on Solana
- Confirm transaction and return explorer link
Signing and Sending Transactions
Deposit and withdraw return encoded unsigned transactions. Normalize then sign/send:
import { VersionedTransaction, Transaction } from "@solana/web3.js";
function extractTransactionString(responseText: string): string {
const trimmed = responseText.trim();
try {
const parsed = JSON.parse(trimmed);
if (typeof parsed === "string") return parsed;
throw new Error("expected transaction string");
} catch (e) {
if (e instanceof SyntaxError) return trimmed;
throw e;
}
}
async function signAndSend(txString: string) {
const bytes = Uint8Array.from(Buffer.from(txString, "base64"));
// Try versioned transaction first, then legacy
try {
const tx = VersionedTransaction.deserialize(bytes);
tx.sign([keypair]);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(sig, "confirmed");
return sig;
} catch {
const tx = Transaction.from(bytes);
tx.partialSign(keypair);
const sig = await connection.sendRawTransaction(tx.serialize());
await connection.confirmTransaction(sig, "confirmed");
return sig;
}
}
Validation loop:
- Build/parse transaction string.
- Try
VersionedTransactionpath. - If it fails, try legacy
Transactionpath. - Confirm transaction.
- If both deserializations fail, return a clear decoding error and do not continue.
Failure handling
400errors: payload issue. Re-check required fields and amount positivity.401/403: wallet/payment authorization issue. Verify wallet and x402 payment capability.402: payment challenge not satisfied. Re-run request through wrapped fetch and do not bypass payment handler.500+: upstream or proxy issue. Retry once with short backoff, then report failure.- Transaction send failure: return explicit error with stage (
deserialize,sign,send, orconfirm).
Response format to user
For successful deposit/withdraw, return:
- Action (
depositorwithdraw) - Token + human amount
- Base-unit amount used in request
- Solana transaction signature
- Explorer URL (
https://solscan.io/tx/{sig})
For balance, return:
- Per-token deposited amount
- Yield earned
- APY details when present
- Note that raw API values are base units and were converted
Supported Tokens
| Token | Mint | Decimals |
|---|---|---|
| USDC | EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v | 6 |
| USDT | Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB | 6 |
| USDS | USDSwr9ApdHk5bvJKMjzff41FfuX8bSxdKcR81vTwcA | 6 |
| SOL | So11111111111111111111111111111111111111112 | 9 |
| JitoSOL | J1toso1uCk3RLmjorhTtrVwY9HJ7X8V9yYac6Y7kGCPn | 9 |
| mSOL | mSoLzYCxHdYgdzU16g5QSh3i5K3z3KZK7ytfqcJm7So | 9 |
| JupSOL | jupSoLaHXQiZZTSfEWMTRRgpnyFm8f6sZdosWBjx93v | 9 |
| JLP | 27G8MtK7VtTcCHkpASjSDdkWWYfoqT6ggEuKidVJidD4 | 6 |
Environment Variables
| Variable | Required | Default | Description |
|---|---|---|---|
WALLET_PRIVATE_KEY | yes | — | Base58-encoded Solana private key |
STRATEGY_ID | no | 43620ba3-354c-456b-aa3c-5bf7fa46a6d4 | Breeze strategy ID — any valid strategy ID works |
X402_API_URL | no | https://x402.breeze.baby | x402 payment API URL |
SOLANA_RPC_URL | no | https://api.mainnet-beta.solana.com | Solana RPC endpoint |
BASE_ASSET | no | USDC mint | Default token mint for operations |
External Endpoints
This skill sends requests to:
https://x402.breeze.baby— Breeze x402 payment-gated API (deposits, withdrawals, balances)https://api.mainnet-beta.solana.com— Solana RPC (transaction signing and broadcasting)
Security & Privacy
This skill requires WALLET_PRIVATE_KEY as an environment variable for signing Solana transactions and x402 USDC micropayments. The key is read from the process environment at runtime and is never logged, printed, or returned in output. By using this skill, small USDC micropayments are sent from your wallet to gate API access. Only install this skill if you trust Breeze with your wallet's signing capability for USDC transactions.
Additional reference
See agent-using-x402 for a full TypeScript implementation.
Overview
You use Breeze x402 to manage Breeze positions directly from your app via a pay-per-request HTTP API. Each protected call to the x402 gateway charges a small USDC micropayment, then returns either API data or an unsigned Solana transaction that you can sign and submit. This skill is intended for developers who need to read balances, initiate deposits, or withdraw assets from Breeze strategies on Solana, while ensuring payments are handled automatically per request.
How This Skill Works
- Prerequisites: You must have Node.js installed and access to the x402 API and a Solana RPC endpoint. Your Solana wallet must be funded with USDC and enough SOL for transaction fees. - Wallet setup: You generate or provide a base58 WALLET_PRIVATE_KEY. This key signs and authorizes your requests and any resulting Solana transactions. - Protected requests with payments: You invoke x402 endpoints (balance, deposit, withdraw, healthz). For each call, Breeze charges a USDC micropayment via the x402 protocol. - Endpoints you’ll use: balance/:fund_user, /deposit, /withdraw, /healthz. Note that the x402 API is distinct from the direct Breeze REST API and uses different authentication and paths. - Response forms: Depending on the endpoint, you receive either structured API data or an unsigned Solana transaction (as a base64 string). - Signing and sending: If the API returns a transaction, you sign it with your private key and broadcast it to the Solana network. You can use a versioned transaction first, with a fallback to a legacy Transaction if needed. - Quick-start flow: prepare and fund a wallet, perform preflight checks, install dependencies, then run a deposit/withdraw flow that returns a transaction to sign and send. - Security posture: Never print or log your WALLET_PRIVATE_KEY. Treat it as a secret; store in a protected .env and add secret files to .gitignore. - Interoperability note: Do not mix x402 with the Direct REST API. They have separate bases, endpoints, and authentication: x402 uses micropayments, while REST uses API keys.
When to Use It
- When you want to check Breeze balances or positions
- When you want to deposit tokens into a Breeze strategy
- When you want to withdraw tokens from a Breeze strategy
- When you want to sign and send the unsigned transaction returned by the API
Quick Start
- Step 0 — Generate or prepare a wallet: Create a funded Solana wallet and save WALLET_PRIVATE_KEY securely (base58). See the included wallet script in the Quick Start for guidance. Step 1 — Preflight checks: Ensure connectivity to x402 and Solana RPC. Step 2 — Install dependencies: npm install @faremeter/fetch @faremeter/payment-solana @faremeter/wallet-solana @faremeter/info @solana/web3.js bs58 --legacy-peer-deps Step 3 — Run a deposit flow: Save a deposit script (deposit.js) as shown in the docs, set WALLET_PRIVATE_KEY in your environment, and run it to receive an unsigned transaction you will sign and broadcast.
- bestPracticesList weblinkNotRequired?null}
- bestPractices.0
- bestPractices
Best Practices
- Treat WALLET_PRIVATE_KEY as highly sensitive: never print or log it. Store it in a secure environment variable (.env) and add wallet-backup.json to .gitignore.
- Protect API URLs and secrets: do not hard-code keys in source; use environment configuration and secret management.
- Verify connectivity before use: run healthz and strategy-info checks to confirm the x402 gateway and Breeze service are reachable.
- Redact secrets when sharing logs after a failure; if a command fails, ensure that logs do not leak private keys or wallet material.
- Prefer signed transactions: when the API returns a transaction, sign it locally and broadcast promptly to minimize risk of drift or nonce issues.
- Keep dependencies up to date and follow the npm legacy-peer-deps note to avoid peer conflicts that could silently remove required packages.
- Separate environments: use distinct wallets and keys for development, staging, and production to reduce blast radius if secrets are exposed.
Example Use Cases
- Example 1 — Check balance: You query the Breeze x402 balance endpoint to view your fund’s current balance, then decide next actions based on funds available.
- Example 2 — Deposit into Breeze: You request a deposit via the x402/deposit flow; the API returns a transaction that you sign and broadcast, updating your Breeze position.
- Example 3 — Withdraw from Breeze: You initiate a withdrawal via the x402/withdraw flow; the API returns a transaction you sign and send to Solana.
- Example 4 — End-to-end deposit and send: You perform a deposit, receive an unsigned transaction, sign it, and then share or view the resulting Solscan link to verify the on-chain activity.