@parad0x_labs/openclaw-x402-gate
v2.0.2
Published
Charge other agents for your OpenClaw skill or API with x402 micropayments on Solana. Mint a 402 challenge, verify the payment, then serve. Funds go straight to your own wallet address — the skill never holds keys or custody.
Maintainers
Readme
openclaw-x402-gate — charge other agents with x402 on Solana
Install on a wallet host with
npm install --ignore-scripts(or--omit=optional). This package runs where your seller key lives;--ignore-scriptsblocks any transitive install-time code. The only native addons are optional and unused here, so nothing is lost. npm can't enforce this from inside a package — it's on you. Details ↓
If it earns its keep, star openclaw-skills — stars are how agent builders find it.
Turn any OpenClaw skill or API into a paid endpoint. Mint an HTTP 402 Payment
Required challenge, verify the payment, then serve. Funds settle straight to
your own wallet on Solana — the skill holds no keys and takes no custody. Pairs
with x402-pay
(the paying side) for the full agent-to-agent loop on a rail that runs on
Solana mainnet-beta (public beta; devnet by default for testing).
Trust model
- No custody.
recipientAddressis your public wallet; payments land there on-chain. This skill signs nothing and holds no keys. - Stateless. Both tools reconstruct the same requirement from config + resource, so receipt hashes match the paying side with no shared state.
- Revenue-grade option. Set
requireOnChain: trueand a payment is accepted only after its transaction is confirmed settled on Solana — not just a well-formed header. The check binds to the unique receipt hash in the tx memo, so proofs can't be replayed against a different charge. - Protocol fee (client-verified). Every settled payment carries a 0.05% (5 bps)
protocol-fee leg to the protocol treasury (a Squads multisig) in the same atomic
transaction as your seller payment.
requireOnChainverifies both legs and refuses to serve if the fee is missing or short — fail-closed on the gating side. This is a client-side on-chain check, not a program-enforced split. The fee amount and treasury are pinned in code on both the paying and gating sides, never read from the challenge, so it can't be zeroed or redirected. Still non-custodial: the fee settles directly on-chain, no one holds it.
Non-custodial. Payments settle straight to your own wallet (less the pinned 0.05% protocol fee, which settles directly to the treasury); the skill holds no keys and signs nothing.
Standalone or together
Part of openclaw-skills. Every skill there is a self-contained module — no imports from sibling skills, own version, own CI lane — so installing, updating, or removing one never breaks another.
- Standalone: yes — this is the selling side; any x402-compliant client can pay your gate, not just our skill.
- Pairs with:
x402-payon the buying agent for the full loop (matching receipt hashes, no shared state). Optional:context-capsuleto keep long selling sessions cheap.
The four tools
x402_challenge({ resource, description? })
→ { status: 402, body } // send `body` to an unpaid caller
x402_verify({ header, resource })
→ { valid, payerAddress, amountUsdc, receiptHash, onChainVerified, capability? }
x402_rep_challenge()
→ { type: "x402-zk-rep", require: { min_count, min_volume, window_start_floor }, trustedRoots }
x402_rep_verify({ proof, publicSignals, expectedAgentCommitment? })
→ { valid, reason?, agentCommitment, reputationNullifier, root, provenMinCount, provenMinVolume }The two x402_rep_* tools gate on a private reputation proof (a Groth16 proof from
x402-pay's prove_reputation) instead of, or alongside, a payment. They need
repMinCount and repMinVolume in config (below); without them they return an error.
Verification runs off-chain in this skill.
Config
{
"plugins": {
"entries": {
"x402-gate": {
"enabled": true,
"config": {
"recipientAddress": "YOUR_SOLANA_WALLET", // required — where funds land
"priceUsdc": 0.05,
"network": "solana-mainnet", // default
"challengeSecret": "<random 32+ char secret>", // REQUIRED on mainnet
"replayStorePath": "/var/lib/x402/replay.log", // REQUIRED on mainnet (durable, single-instance)
"acknowledgeSingleInstance": true, // confirm you run ONE instance
"rpcUrl": "https://<your-private-rpc>" // REQUIRED on mainnet
}
}
}
}
}| Key | Default | Description |
|---|---|---|
| recipientAddress | — (required) | Your wallet; payments settle here. Public key only. |
| priceUsdc | 0.01 | Default price per request (USDC) |
| network | solana-mainnet | Settlement network. Set solana-devnet to test |
| requireOnChain | true | Confirm settlement before serving. Refused on mainnet if false (header-only mode is devnet/testing only) |
| dedupe | true | Single-use payment guard. On mainnet needs a durable store (below) |
| replayStorePath | — | Restart-durable, single-instance replay store path. Required on mainnet with dedupe |
| acknowledgeSingleInstance | false | Attest you run ONE instance (the file store isn't shared across replicas). Required on mainnet with dedupe |
| acknowledgeExternalReplayStore | false | Attest you run your own shared store. Required on mainnet with dedupe: false |
| requirePresenterAuth | true | Caller signs the nonce with the payer key. Forced on for mainnet |
| challengeSecret | — | MACs nonces + capability tokens. Required on mainnet |
| receiptScopeSeconds | 0 | >0 issues a reusable capability token (pay once, reuse within scope) |
| rpcUrl | — | Solana RPC for settlement checks. Required on mainnet (use a private node); devnet defaults to the public devnet endpoint |
| repMinCount | — | zk-rep: receipt-count floor a reputation proof must meet. Set with repMinVolume to enable the x402_rep_* tools |
| repMinVolume | — | zk-rep: total-volume floor (atomic USDC units) |
| repWindowSeconds | 90 days | zk-rep: how far back proven receipts may start |
| repTrustedRoots | — | zk-rep: anchored receipt-tree roots the gate trusts; a proof against any other root is rejected |
Plugin settings go under config in the plugin entry: OpenClaw validates
plugins.entries.x402-gate.config against the schema in openclaw.plugin.json and
rejects unknown keys.
Mainnet is fail-closed. On
solana-mainnetthe gate refuses to serve (every call returns an error) unless:requireOnChainis true; replay is durable (replayStorePath+acknowledgeSingleInstance, ordedupe: false+acknowledgeExternalReplayStorewith your own shared store); achallengeSecretand an explicitrpcUrlare set. Header-only mode (requireOnChain: false) is allowed only on devnet.
Scaling: single instance vs. multiple replicas
The built-in replay store (replayStorePath → FileReplayStore) is per-instance:
it lives on one process's disk and is not shared across replicas. Running two
copies against the same path would let one settled payment be redeemed once per
replica. To prevent that, FileReplayStore takes a single-instance lock on
startup — a second instance pointed at the same path refuses to start.
- Single instance (default): set
replayStorePath+acknowledgeSingleInstance: true. Done. - Multiple replicas / load-balanced: do not use
replayStorePath. Setdedupe: false+acknowledgeExternalReplayStore: trueand enforce replay in your own shared atomic store (RedisSETNX/ a DB unique constraint) around the returnedsignature/receiptHashbefore serving.
Capabilities (
receiptScopeSeconds > 0) are single-instance only — they requirededupe: true+replayStorePath+acknowledgeSingleInstanceand the gate refuses them in multi-replica (dedupe: false) mode.replayStorePathmust be local disk; a shared/network filesystem across hosts defeats the lock.
A capability is a PRE-PAID reuse window at the price paid. It grants reuse for
receiptScopeSecondsfrom the settling payment, and a laterpriceUsdcchange does not retroactively re-price or revoke an outstanding capability (the buyer already paid for that term). SetreceiptScopeSecondsto the longest window you're willing to pre-sell at the current price — that is your exposure bound.
Install safety
This skill runs where your seller wallet lives. Install so no transitive native addon can run install-time code on that host — use either:
npm install --ignore-scripts @parad0x_labs/openclaw-x402-gate # skips ALL install scripts
npm install --omit=optional @parad0x_labs/openclaw-x402-gate # skips the optional native addonsThis is required, not optional for a key-holding host. The only transitive
native builds are bufferutil / utf-8-validate — optional perf addons of
@solana/web3.js's WebSocket stack; this skill makes only HTTP RPC calls, so they
are never loaded, and skipping them loses nothing.
npm cannot enforce this from inside a published package — a dependency's
overrides/scripts/.npmrcdon't govern your install, only your root project's do. Treat--ignore-scriptsas policy on any wallet host, and prefer building on a host that does not hold the seller key.
Flow
- Caller hits your gated capability with no payment →
x402_challengereturns a 402 telling them the price and your address. - Caller pays (e.g. via
openclaw-x402-pay) and retries with anX-Paymentheader. x402_verifychecks it. WithrequireOnChain, it confirms the transaction settled before you serve the resource.
Operations
- Rate-limit
x402_verify. WithrequireOnChain, each call may cost onegetTransactionon your metered RPC. Presenter-auth limits callers to keypair holders (each attempt costs them a signature) but does not rate-limit — put a per-source / per-payer limit in front of the endpoint so an unpaid caller can't run up your RPC bill. - Run one instance per
replayStorePath. The file replay store is single-writer; the gate refuses to start if another live process holds the lock. For multiple replicas, usededupe=false+ your own shared store (Redis/DB) and setacknowledgeExternalReplayStore=true.
No external @parad0x_labs dependency
Constants and wire types are vendored inline. The runtime dependencies are
@solana/web3.js (on-chain confirmation), bs58 (decoding the SPL Memo instruction
for program-attested receipt binding), typebox (tool parameter schemas), and
snarkjs + poseidon-lite (track-record proof verification).
Source
github.com/Parad0x-Labs/openclaw-skills/tree/main/skills/x402-gate
