Docs

Agents

spaceturtle

The connector, by hand

Get paid

A node anyone on Base or Solana can open a channel to and pay, with no gas.

A price makes a route cost something. A settlement backend is what lets anyone actually pay it. There are two chains, and a node may carry either table or both; with both, it accepts vouchers on both at once. Every channel is an x402 batch-settlement channel: on EVM that is x402's audited x402BatchSettlement contract, and on Solana the Solana Foundation's payment-channels program. Both are constants of the binary, the same address on every network, so there is nothing chain-specific to name beyond your own RPC, token and key.

The short version: add the eight-line settlement block below to connector.toml, give its key a little gas, and restart. connector init --devnet writes it for you. The rest of this page is what each line commits you to.

The shape

StepWho actsWhat touches the chain
Termsclient readsNothing: GET /ilp, or the 402 greeting on an unpaid request.
OpenclientEVM: one deposit, relayed by an x402 facilitator. Solana: one open, co-signed by your node.
PayclientNothing: one signed voucher per paid packet, carried inside ILP.
Landyour nodeBatched claim + settle (EVM), settle (Solana), every ten minutes.
ExitclientEVM: initiateWithdraw, then finalizeWithdraw. Solana: request_close, then the payer's withdrawal.
Exit raceyour nodeLands its latest voucher as soon as it sees the exit start.

Value moves one way on a channel, client to you. Claims are frequent and free, and settlement is rare. The chain sees the deposit, the sweeps, and the exit.

The settlement block

connector.tomldevnet values
# EVM, Base Sepolia. These are live devnet addresses, not placeholders.
[settlement.evm]
rpc_url               = "https://base-sepolia-rpc.publicnode.com"
token_address         = "0x0C996d7c934c79a6255254875607Fe69df25C0E1"  # the token every price is in
decimals              = 6              # units per token: 6 means 1,000,000 = 1.00
asset_eip712_name     = "USDC"         # the token's own EIP-712 domain:
asset_eip712_version  = "2"            # cast call <token> 'name()(string)' / 'version()(string)'
# min_withdraw_delay_secs = 86400      # default one day; 900 at least, 30 days at most
# asset_transfer_method = "eip3009"    # "permit2" for a token without ERC-3009
# facilitator_url = "https://..."      # the x402 facilitator payers relay deposits through

[settlement.evm.key]
key_file = "/app/data/settlement.key"

# Solana, public devnet.
[settlement.solana]
rpc_url               = "https://api.devnet.solana.com"
token_address         = "34eSxY7qxQ4GzyhDJ8GpUcTz1WWzruGbJbR8q6TtxfQU"  # an SPL mint; Token-2022 is refused
decimals              = 6
min_sponsored_deposit = 1000000        # base units: the smallest deposit whose open you will pay rent for
# min_grace_period_secs = 86400        # default one day; 900 at least

[settlement.solana.key]
key_file = "/app/data/settlement-solana.key"

Copy the token address rather than picking your own: a voucher resolves against one token, so every node that might accept a given voucher has to name the same one. The two asset_eip712_* keys and min_sponsored_deposit have no default and are required wherever their table exists. Read the EIP-712 name and version off the token itself: Base mainnet's native USDC is "USD Coin" / "2", not "USDC". A wrong name shows up only as every client's deposit failing.

decimals is what turns a price into money. The node reads the token's own decimals at boot and refuses to start if the config disagrees, because a wrong value misprices every route by a factor of ten or more. Every other settlement value is verified against the chain the same way, which is why there is no --network flag: which chain a node is on is these values. The node confirms x402BatchSettlement's code is present at its fixed address before it will serve.

Chains

EVMSolana
DevnetBase Sepolia, chain id 84532public devnet
MainnetBase, chain id 8453mainnet-beta
Channels live inx402BatchSettlement, the same address on every networkpayment-channels, the same program id on every cluster
Devnet token0x0C996d7c…C0E1, devnet USDC (Circle FiatToken v2.2, ERC-3009), 6 dp34eSxY7q…xfQU, mock USDC mint, 6 dp
Funding the keyBase Sepolia ETH for gas; devnet USDC from the faucetdevnet SOL (solana airdrop); mock USDC from the same faucet

There is no Solana testnet deployment: payment-channels is on devnet and mainnet-beta, nowhere else. Mainnet exists on both chains and the live node at toon.ar.io settles there. Because the contract and program are the same address everywhere, the one thing that has to match the network is the token: on mainnet, point token_address at Base mainnet's USDC or Solana mainnet-beta's, never the devnet one. The x402 Foundation lists x402BatchSettlement at one address on Base, Arbitrum, Polygon, Optimism, Avalanche, Celo, Linea, Unichain, Monad and World Chain; the node will serve on any of them where the code is present.

How a channel is admitted

You do not list the channels your payers will use, and you could not: a client's channel does not exist until that client opens it toward you. A voucher naming a channel you have never heard of is resolved from chain and admitted, which is what makes paying you permissionless rather than an arrangement. Your node only admits a channel whose terms are the ones it published:

ChainThe channel must have
EVMreceiver and receiverAuthorizer = your settlement address; token = your token_address; withdrawDelay ≥ your minimum; a nonzero payerAuthorizer. That last rule is stricter than x402. The client's deposit is relayed by the facilitator you name; a payer holding ETH can deposit directly instead. The first voucher on a channel your node has not seen carries the full channelConfig.
SolanaThe client builds an open in which your settlement key is fee payer, rent_payer and payee, signs it, and posts it to POST /ilp/batch-settlement/solana/open, which the greeting names as extra.sponsorEndpoint. The node checks every field, co-signs, submits, waits for confirmation, and admits it. The endpoint is public, bounded by a cap of 8 sponsorships in flight, 1 per payer, and an hourly budget of failed opens. Only SPL Token mints; Token-2022 is refused.

How a voucher is checked. A voucher has no nonce. It is accepted only if its amount is strictly greater than the last one accepted on that channel, by at least the route's charge. A replayed voucher buys nothing. A packet to a free route carries no voucher at all. A client that lost track asks POST /ilp/claim-state to learn the amount it has to beat.

How you get paid

Automatically, with no operator write, for what lands on chain:

ChainCadence
EVMEvery 5 s, read new WithdrawInitiated logs and claim that channel's latest voucher at once, because only the amount already claimed survives the withdrawal. Every 10 min, claim every held voucher above what the chain records, many channels in one transaction, then settle to your address. The sweep also runs at boot.
SolanaEvery 10 s, rediscover every channel you sponsored from the chain. Open: settle the latest voucher every 10 min. Closing: settle_and_seal inside the grace period. Sealed: distribute. Distributed: reclaim the channel's rent.

The one-day default minimum for withdrawDelay and grace_period is the window a delayed or censored landing transaction still has. POST /channels/:id/land is the manual lever for planned maintenance.

Who pays gas, and why

x402BatchSettlement has no fee field. Nothing on chain pays a facilitator, so gas is paid by whoever wants the transaction to happen. In TOON that is the seller: your node names the facilitator it relays deposits through, and deposit gas is a cost of the sale. Your node publishes that URL and never calls it; the payer does.

StepWho pays
EVM deposit or top-upThe facilitator you name, off chain at your expense; or the payer, from its own ETH.
EVM Permit2 approval (once)The facilitator, if it sponsors approvals for this token; otherwise the payer's own ETH.
Solana openYou: the transaction fee and 4,711,920 lamports (about 0.0047 SOL) of rent per channel, returned at reclaim.
A voucherNobody: a signature.
EVM claim and settleYou, from the EVM settlement key's ETH.
Solana settle, seal, distribute, reclaimYou, from the Solana settlement key's SOL.
The payer's exitThe payer.
Your own outbound channelsYou: when your node pays a peer, it deposits directly and pays its own gas.

Choosing a facilitator

The facilitator must never be your receiverAuthorizer. That key can refund to the payer anything you have earned and not yet claimed. Your node always names its own settlement address, so a facilitator that advertises its own authorizer is simply not used as one.
  • Run a stock x402 facilitator yourself. TOON's Onboarder is one; the devnet runs it at onboard.devnet.toonprotocol.dev. It is the published @x402/evm facilitator with no receiverAuthorizer. Its gas key needs ETH, and that ETH is the cost of sale.
  • Use a hosted one. Several hosted x402 facilitators serve Base mainnet. Check that the one you pick lets you keep your own receiver authorizer, and check its current terms with the provider.
  • Name none. Then only a payer that brings its own facilitator, or holds ETH, can deposit. The client falls back, in order, to the payer's own facilitator, then yours, then the devnet Onboarder on Base Sepolia only.

Solana needs none of this. The payer posts its open to your sponsor endpoint, and your node is the fee payer.

Tokens

Tokenasset_transfer_methodIs the payer's first deposit gasless?
ERC-3009 (receiveWithAuthorization): Circle's USDC, the devnet USDC"eip3009" (default)Yes, through any facilitator. One signature, no approval.
EIP-2612 permit, no ERC-3009"permit2"Yes, if the facilitator offers eip2612GasSponsoring. Otherwise the payer sends a one-time Permit2 approval from its own ETH.
Any other ERC-20"permit2"Yes, if the facilitator offers erc20ApprovalGasSponsoring for this token. Otherwise the payer's own ETH.
Solana: an SPL Token mintn/aYes. Your node sponsors the open. Token-2022 mints are refused.

Checking it

curl -s https://your-node.example/ilp | jq '.batchSettlements, .voucherSigners'

Check that there is one batchSettlements entry per chain you configured, with your settlement address as payTo (EVM) or your key as feePayer (Solana), and the name, version, assetTransferMethod and facilitator you meant to publish. After a client has paid, read the journal with your bearer token:

curl -s -H "Authorization: Bearer $TOKEN" https://your-node.example/claims | jq

Each paying channel shows "scheme": "batch-settlement" and a cumulative_amount that rises with every packet. Watch that watermark, not the count of claims.

SymptomLikely cause
Every client's EVM deposit failsWrong asset_eip712_name/version, or "eip3009" on a token without ERC-3009.
Permit2 payers are told they need ETHYour facilitator does not sponsor approvals for this token.
First voucher refused: "does not admit"The client's ChannelConfig names a zero payerAuthorizer, a short withdrawDelay, another token, or another receiver.
"does not strictly exceed this channel's watermark"The client lost its store: it should ask POST /ilp/claim-state.
Solana open: receiving_account_unusableYour own token account for the mint does not exist, or is frozen: create it.
Solana open: sponsor_pausedEight sponsored opens failed on chain in the last hour.
"could not look up the channel's counterparty"Your settlement RPC is not answering; not the client's fault.

Before you rely on it

Fund the settlement key before you start the node, and know that booting a config is not a dry run. A Solana backend submits a real transaction at connect; with no gas the connector exits on a chain error that reads like a config bug. With a funded key, starting a node "just to see whether the TOML parses" spends real money.
  • One way per channel. To pay a client back, open a channel of your own toward the terms it publishes (POST /channels).
  • Solana costs SOL. Each sponsored channel locks about 0.0047 SOL of rent until reclaimed. Your node's token account for the mint must already exist.
  • Third-party code. The EVM contract is ownerless and immutable; nothing can rescue an escrow that USDC's blacklist has frozen. payment-channels can be upgraded, on mainnet-beta by a 3-of-5 multisig with no time lock. What you risk as a receiver is whatever you have accepted in vouchers and not yet landed on chain.
  • An ILP outcome is never an HTTP one. A FULFILL and a REJECT both come back at HTTP 200. A caller with no voucher on a priced route gets 402 with the greeting.

Written from connector/docs/guide/get-paid.md and connector/docs/operators/x402-batch-settlement.md. The wire rules are client-edge-spec.md §1.3, §1.4, §1.10 and §1.11; the keys are configuration-spec.md.