ZeroClaw plugins · custody tier T1 · Solana + Base

Multi-chain Payment Terminal
for your self-hosted agent

Two ZeroClaw tool plugins let an agent charge a customer from any chat channel on Solana or Base L2 — the plugin builds the payment-request URL, the customer's wallet signs, and the agent confirms payment on-chain. Zero private keys held.

▶ Demo video — 2 minutes 33 seconds

The full payment-terminal flow on camera: real plugin · real RPC · a real finalized devnet transaction — no mocked receipts. Solana Pay charge → customer QR → on-chain confirmation, plus the Base L2 (EIP-681) counterpart.

▶  PLAY THE DEMO

Hosted .mp4 (720p H.264) · direct video link · mirror

What it does

A self-hosted ZeroClaw agent acts as a payment terminal on a chat channel (WhatsApp/Telegram). Staff message the agent to charge a customer; the agent generates a payment-request URL (Solana Pay or EIP-681 on Base), the customer's wallet scans the QR and pays, and the agent confirms payment on-chain — all with zero private keys held (custody tier T1).

Who it's for

Small businesses and family shops that accept USDC or SOL, freelancers who want a payment-link bot, and any operator who wants "charge X, confirm paid" without exposing a hot wallet.

Why it's safe

The plugins never sign and never broadcast. A prompt-injected agent cannot move funds — it has no keys to move. The worst case is an extra payment request a human still has to approve.

How it's built

Two Rust WIT components (wasm32-wasip2): solana_pay_tool.wasm (350 KB) + base_usdc_tool.wasm (371 KB). Pure logic cores with 84 passing tests total, no wasm or HTTP dependency in the test path.

Two operations, one tool

opWhat it doesCustody
create_paymentBuilds a solana: transfer-request URL with a unique reference keyT1 — no keys held
check_paymentPolls getSignaturesForAddress on the reference key to confirm paidT0 — RPC read only

The reference key is the trick that makes payment detectable without a server: the payer's wallet includes it in the transfer instruction, and a cron SOP polls for a confirmed signature on that key every 10 seconds.

Now multi-chain: Solana + Base L2

The base-usdc-tool plugin is the Base L2 counterpart to solana-pay-tool — same terminal pattern, same custody tier, different chain. Together they give a ZeroClaw agent two-chain payment coverage with one mental model:

ChainPluginPayment URL formatVerification method
Solanasolana-pay-toolsolana:<recipient>?amount=…&reference=…getSignaturesForAddress on the reference key
Base L2base-usdc-toolethereum:<usdc>@8453/transfer?address=<to>&uint256=<raw>eth_getTransactionReceipt / chunked eth_getLogs on USDC Transfer events

The Base flow (EIP-681)

Staff message: "charge customer, 25 USDC." The agent calls base_usdc with op=create_payment; the plugin returns an EIP-681 URL targeting the native USDC contract (0x833589fC…A02913) on chain 8453. The agent posts it as a deep link / QR; the customer's wallet (MetaMask, Rainbow, …) opens, previews the USDC transfer, and signs. A cron SOP polls check_payment every 30s: eth_getTransactionReceipt confirms status, and an eth_getLogs scan (4500-block chunks — free RPCs reject wider ranges) matches the Transfer(address,address,uint256) event to the shop wallet with the exact 6-decimal amount. Paid → "Invoice #412 paid ✓."

Base demo — the exact EIP-681 URL base-usdc-tool builds

Same builder logic as the plugin's create_payment (rendered in your browser; no keys involved, nothing is signed). Scan with any EVM wallet to see the USDC transfer preview on Base. Demo shop wallet — do not send real funds.

The plugin also converts the decimal amount to raw uint256 (6 decimals) exactly as shown below — the same conversion its unit tests pin down.

Screenshot of the live Base EIP-681 QR demo
The Base demo above: an EIP-681 payment-request URL for native USDC on Base (chain 8453), rendered as a scannable QR.

Solana demo — the exact URL solana-pay-tool builds

This is the same Solana Pay transfer-request format create_payment produces (here rendered in your browser; no keys involved, nothing is signed). Scan it with Phantom or Solflare on devnet to see the wallet preview.

Screenshot of the live Solana Pay QR demo
The live demo above: a fresh reference key per request, rendered as a scannable Solana Pay QR.

Build & test evidence

solana-pay-tool (Solana)

cargo test output — 28 tests passing
cargo test on the Solana plugin: 14 unit + 14 integration tests, all passing. Logic lives in a pure Rust module (src/solana_pay.rs) so the full suite runs natively.
Compiled wasm32-wasip2 component artifact
The compiled Solana component: solana_pay_tool.wasm, 350,929 bytes, built for wasm32-wasip2.

base-usdc-tool (Base L2)

cargo test output — 56 tests passing
cargo test on the Base plugin, re-run 2026-07-30: 32 unit + 24 integration tests, all passing — URL construction, raw-amount conversion, topic padding, receipt/log parsing, config overrides, and rejection of bad addresses / zero amounts.
Compiled base_usdc_tool wasm32-wasip2 component artifact
The compiled Base component: base_usdc_tool.wasm, 370,995 bytes, built for wasm32-wasip2.

End-to-end host test — Base mainnet (4/4 PASS)

The compiled component was loaded into a real ZeroClaw host (wasmtime + cranelift) and run end-to-end against live Base mainnet: EIP-681 URL construction, live eth_blockNumber/eth_getLogs payment check (correctly unpaid), and both error paths (invalid / missing recipient) — 4/4 tests passed. Full transcript: e2e_base_usdc_transcript.txt.

Deadline-day regression re-verified 2026-08-07: 8/8 PASS — the full end-to-end suite was re-run on the submission deadline day against live RPCs on both chains: Solana devnet 4/4 (URL construction, unpaid poll, error paths) + Base mainnet 4/4 (suite above). Nothing regressed after the base-usdc-tool vendoring; both plugins are submission-ready.

Custody tier & threat model

Tier: T1 (Build). The plugin constructs unsigned Solana Pay URLs and reads on-chain state. It never holds a private key, never signs, never broadcasts.

Reproduce it in an evening

Solana (solana-pay-tool)

# 1. Clone the canonical repo (repo root IS the solana-pay plugin)
git clone https://github.com/jakesullivan77/zeroclaw-solana-pay
cd zeroclaw-solana-pay

# 2. Build the component
rustup target add wasm32-wasip2
cargo build --target wasm32-wasip2 --release

# 3. Install into ZeroClaw (source-built host, plugins-wasm-cranelift)
zeroclaw plugin install ./solana-pay-tool/
zeroclaw config set plugins.enabled true

# 4. Configure (optional: custom RPC, USDC mint)
zeroclaw config set plugins.entries.solana-pay-tool.rpc_url https://api.devnet.solana.com
zeroclaw config set plugins.entries.solana-pay-tool.spl_token EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v

# 5. Tell the agent
> Charge table 4, 25 USDC

Base L2 (base-usdc-tool)

# Vendored in the same canonical repo: plugins/base-usdc-tool/
git clone https://github.com/jakesullivan77/zeroclaw-solana-pay
cd zeroclaw-solana-pay/plugins/base-usdc-tool

# Same toolchain pattern — build, install, configure
rustup target add wasm32-wasip2
cargo build --target wasm32-wasip2 --release
# → target/wasm32-wasip2/release/base_usdc_tool.wasm (370,995 bytes)

zeroclaw plugin install ./base-usdc-tool/
# Optional config (defaults shown; operator-set, encrypted at rest)
zeroclaw config set plugins.entries.base-usdc-tool.rpc_url https://mainnet.base.org
zeroclaw config set plugins.entries.base-usdc-tool.usdc_address 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913

# Tell the agent
> Charge customer, 25 USDC on Base

Also in the repo: the skill markdown that teaches the agent the payment flow, and the cron SOP that polls check_payment (every 10s on Solana, every 30s on Base). The base-usdc-tool plugin ships with its own skill (skills/base-usdc-terminal.md) and cron SOP (sops/payment-watch.cron.md).