Testing an agent that pays
The other commands test a service that sells. wasit serve tests the other
side, an agent that pays: it runs a local x402 paywall that misbehaves in one
chosen way, so you can point your agent at it and watch what the agent does.
wasit serve --mode no-settle| Mode | What the paywall does | What a careful agent does |
|---|---|---|
no-settle | Issues an honest challenge, then serves the resource for any payment without settling it and without a PAYMENT-RESPONSE header | Finds no PAYMENT-RESPONSE and does not count the payment as made |
wrong-settlement | Issues an honest challenge, then serves the resource with a PAYMENT-RESPONSE reporting success for a transaction that is not this payment | Looks the transaction up on-chain before trusting it |
wrong-network | Asks to be paid on stellar:pubnet (mainnet) instead of stellar:testnet | Refuses before signing anything |
overprice | Asks for one million USDC (10000000000000 base units) | Refuses a price above its spending limit |
v1-challenge | Issues its challenge only in the x402 v1 form, a JSON body with v1 field names and network name (base-sepolia, solana-devnet) and no PAYMENT-REQUIRED header, as a paywall on the old SDK does. Base Sepolia and Solana devnet only: v1 names no Stellar network | Pays it as v1, in an X-PAYMENT header, or declines; does not answer it with a v2 payment |
malformed-header | Sends a PAYMENT-REQUIRED header that does not decode: base64 of the challenge's JSON, cut short | Reports the challenge as unreadable and pays nothing |
| Option | Default | Notes |
|---|---|---|
--mode <mode> | required | One of the six above |
--port <port> | 4020 | The server answers on every path |
--host <host> | 127.0.0.1 | Local only unless you bind another interface |
--network <id> | stellar:testnet | stellar:testnet, eip155:84532 (Base Sepolia), eip155:11155111 (Ethereum Sepolia) or solana:EtWTRABZaYq6iMfeYKouRu166VU2xqa1 (Solana devnet). Elsewhere than Stellar the paywall uses that network's USDC (the SDK's, or Circle's on Ethereum Sepolia), wrong-network asks for that chain's mainnet (eip155:8453, eip155:1, solana:5eykt4UsFv8P8NJdTREpY1vzqKqZKvdp), and overprice one million USDC in its 6 decimals. On Solana the challenge names the payee as extra.feePayer, which the spec allows; nothing is ever submitted |
--pay-to <address> | STELLAR_PAYEE_ADDRESS, EVM_PAYEE_ADDRESS on Base or Ethereum Sepolia, or SVM_PAYEE_ADDRESS on Solana devnet | A testnet account (G...) with a trustline for the asset, an EVM address (0x...), or a Solana address (base58) |
--amount <units> | 10000 | Price in base units, for every mode except overprice |
--asset <contract> | testnet USDC | CBIELTK6YBZJU5UP2WWQEUCYKLPU6AUNZ2BQ4WWFEIE3USCIHMXQDAMA |
--settlement-tx <hash> | a random hash | wrong-settlement only. The default cites a transaction that exists nowhere; pass a real, unrelated transaction to test an agent that finds it on-chain but must notice it is not this payment |
Nothing is settled or forwarded in any mode, so no payment the server receives
can move funds. Your agent still needs a funded testnet wallet: payment
clients build and simulate the transfer to --pay-to before signing, which is
also why the payee must exist on testnet with a trustline for the asset.
The server prints one line per request: the challenge it sent, or the payment it received and what that says about the agent:
wasit serve: no-settle on http://127.0.0.1:4020/ (any path)
01:02:46 GET /paid: payment received for 10000 base units on stellar:testnet.
Served 200 without settling and without PAYMENT-RESPONSE. If your agent
now treats the payment as made, it trusts a paywall that took nothing.The first four modes' challenges are well-formed x402 v2 challenges (wasit test --read-only passes them), so an agent that falls for one fell for the
misbehaviour, not for a malformed message. Pointing wasit test itself at
no-settle or wrong-settlement shows what a non-conformant paywall looks
like from the checks' side: X402-06 and X402-07 fail. The last two are
about the challenge itself: v1-challenge is a complete v1 challenge
(X402-04 reports every v1 field present) and malformed-header fails
X402-03. They answer any payment with 402 and log which header it came in.
The official x402 SDK client pays v1-challenge as v1 and refuses
malformed-header without paying
(evidence).