
Agentic Wallet
- 2.3k installs
- 122 repo stars
- Updated August 4, 2026
- coinbase/agentic-wallet-skills
Agentic Wallet is a Coinbase agent skill that exposes awal CLI wallet sign-in, balance checks, token transfers, swaps, funding, and x402 paid-API flows on Base for developers building crypto-enabled agents.
About
Agentic Wallet is a Coinbase agentic-wallet-skills module that lets coding agents perform wallet operations through the awal CLI without leaving the chat. Developers can sign in, read balances and addresses, send USDC, ETH, POL, or SOL, swap tokens, fund wallets via onramp, resolve ENS names, and pay for APIs using the x402 bazaar protocol on Base. The skill is user-invocable and triggers on authentication, balance, transfer, trade, funding, or paid-endpoint keywords. It fits agent builders adding onchain payments, micropaid API access, or treasury automation to backends and bots. Outputs include signed transactions, balance snapshots, x402 payment receipts, and onchain query results agents can chain into larger workflows.
- Routes 12+ crypto operations (sign-in, balances, transfers, trades, funding, x402 payments, onchain queries) via awal CL
- Mandatory preflight status check before any authenticated wallet action
- Reference-driven routing: reads auth.md, balances.md, transfer.md, trade.md, x402.md or onchain.md before acting
- Supports USDC, ETH, POL, SOL plus ENS names and Base network
- Enables agents to discover and pay for paid services or monetize their own APIs
Agentic Wallet by the numbers
- 2,346 all-time installs (skills.sh)
- +325 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Ranked #425 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/coinbase/agentic-wallet-skills --skill agentic-walletAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 2.3k |
|---|---|
| repo stars | ★ 122 |
| Last updated | August 4, 2026 |
| Repository | coinbase/agentic-wallet-skills ↗ |
How do agents pay for APIs onchain?
Let their coding agent securely sign in, check balances, send tokens, trade, fund a wallet, pay for APIs via x402, or query onchain data on Base without leaving the cha
Who is it for?
Developers building agents that need programmatic wallet auth, Base-chain transfers, or x402 micropayments without custom wallet SDK code.
Skip if: Teams avoiding crypto custody in agent runtimes or products with no onchain payment requirement.
When should I use this skill?
A developer mentions awal login, wallet balance, sending USDC/ETH, token swaps, wallet funding, x402 paid APIs, or onchain data on Base.
What you get
Authenticated wallet sessions, token transfer receipts, swap results, funded balances, and x402 payment records.
- Signed transactions
- Balance reports
- x402 payment receipts
Files
Agentic Wallet
Operate a crypto wallet through the awal CLI. This skill is a router: read the relevant reference file in references/ for the task at hand.
Preflight: Confirm wallet state
Before any wallet operation that requires authentication (everything except x402 search/details), check status:
npx awal@2.12.0 statusIf the wallet is not authenticated, read references/auth.md and complete sign-in first.
Routing
Pick the reference that matches the task and Read it before acting:
| Task | Reference |
|---|---|
| Sign in, log in, connect wallet, OTP verification, "not signed in" errors | references/auth.md |
| Check balances, "how much USDC/ETH/POL/SOL do I have", balance per chain, JSON balance output | references/balance.md |
| Send USDC / ETH / POL / SOL to an address or ENS name (Base, Polygon, Solana) | references/send-usdc.md |
| Swap / trade / convert tokens on Base or Polygon | references/trade.md |
| Add funds, top up, onramp, buy USDC | references/fund.md |
| Find / browse / search paid services on the x402 bazaar | references/x402-search.md |
| Call a paid x402 API endpoint with automatic USDC payment | references/x402-pay.md |
| Build or deploy a paid API server that other agents can pay to use | references/x402-monetize.md |
| Query onchain data on Base (events, transactions, blocks) via the CDP SQL API | references/query-onchain.md |
If no clear match and the user wants an external capability, search the x402 bazaar (references/x402-search.md) — a paid service may exist.
Shared rules
- Input validation: every reference lists the regexes / allowlists that user-provided values must match before being placed in a shell command. Validate strictly; reject inputs containing spaces, semicolons, pipes, backticks, or other shell metacharacters. Do not pass unvalidated user input into commands.
- Single-quote `$` amounts: any amount written as
'$1.00'must be single-quoted to prevent bash variable expansion. - JSON output: every
awalcommand supports--jsonfor machine-readable output. - Auth errors mean re-auth: if any command fails with "Not authenticated" or similar, read
references/auth.mdand run the sign-in flow. - Insufficient balance: read
references/fund.mdto top up.
Quick command index
| Command | Purpose |
|---|---|
npx awal@2.12.0 status | Server health + auth status |
npx awal@2.12.0 address | Get wallet address |
npx awal@2.12.0 balance | Get balances across Base, Polygon, Solana (use --chain for one chain) |
npx awal@2.12.0 show | Open the wallet companion window (used for funding) |
npx awal@2.12.0 auth login <email> | Send OTP code |
npx awal@2.12.0 auth verify <otp> | Complete sign-in |
npx awal@2.12.0 send <amount> <recipient> | Send tokens |
npx awal@2.12.0 trade <amount> <from> <to> | Swap tokens |
npx awal@2.12.0 x402 bazaar search <query> | Search paid services |
npx awal@2.12.0 x402 bazaar list | List bazaar resources |
npx awal@2.12.0 x402 details <url> | Inspect payment requirements |
npx awal@2.12.0 x402 pay <url> | Pay and call an x402 endpoint |
Authenticating with the Agentic Wallet
When the wallet is not signed in (detected via npx awal@2.12.0 status or when wallet operations fail with authentication errors), use the npx awal CLI to authenticate.
If you have access to email, you can authenticate the wallet yourself, otherwise you'll need to ask your human to give you an email address and to tell you the OTP code they receive.
Authentication Flow
Authentication uses a two-step email OTP process:
Step 1: Initiate login
npx awal@2.12.0 auth login <email>This sends a 6-digit verification code to the email. The flowId is saved automatically; it is only printed to stdout when --json is passed.
Step 2: Verify OTP
npx awal@2.12.0 auth verify <otp>Use the 6-digit code from the user's email to complete authentication. The flow ID from step 1 is saved automatically to a local file — you do not pass it as an argument. If you have the ability to access the user's email, you can read the OTP code, or you can ask your human for the code.
Input Validation
Before constructing the command, validate all user-provided values to prevent shell injection:
- email: Must match a standard email format (
^[^\s;|&]+@[^\s;|&]+$). Reject if it contains spaces, semicolons, pipes, backticks, or other shell metacharacters. - otp: Must be exactly 6 digits (
^\d{6}$).
Do not pass unvalidated user input into the command.
Checking Authentication Status
npx awal@2.12.0 statusDisplays wallet server health and authentication status including wallet address.
Example Session
# Check current status
npx awal@2.12.0 status
# Start login (sends OTP to email)
npx awal@2.12.0 auth login user@example.com
# Output: "Verification code sent!" (flowId only printed with --json)
# After user receives code, verify (flow ID saved automatically)
npx awal@2.12.0 auth verify 123456
# Confirm authentication
npx awal@2.12.0 statusSigning Out
There is no `awal auth logout` CLI command today. The agent cannot log the user out programmatically — sign-out must be performed by the human in the wallet companion UI.
When the user asks to log out, sign out, disconnect, or switch accounts:
npx awal@2.12.0 showThen guide the user through the UI:
1. Run npx awal@2.12.0 show to bring the wallet companion window to the foreground. 2. Tell the user to open the wallet menu (settings / profile area in the companion window). 3. Have the user click Sign out (or Log out) inside that window. 4. Confirm the result with npx awal@2.12.0 status — once logged out, the status will report the wallet as not authenticated.
After sign-out, the locally cached flowId is invalidated. To sign back in, restart the flow with npx awal@2.12.0 auth login <email>.
If npx awal@2.12.0 show does not bring up a window (e.g. running in a non-graphical environment), let the user know that logout requires the wallet companion UI and is not currently scriptable.
Available CLI Commands
| Command | Purpose |
|---|---|
npx awal@2.12.0 status | Check server health and auth status |
npx awal@2.12.0 auth login <email> | Send OTP code to email, returns flowId |
npx awal@2.12.0 auth verify <otp> | Complete authentication with OTP code |
npx awal@2.12.0 balance | Get balances across Base, Polygon, and Solana (use --chain for a single chain) |
npx awal@2.12.0 address | Get wallet address |
npx awal@2.12.0 show | Open the wallet companion window (also used for sign-out via the UI) |
JSON Output
All commands support --json for machine-readable output:
npx awal@2.12.0 status --json
npx awal@2.12.0 auth login user@example.com --json
npx awal@2.12.0 auth verify <otp> --jsonChecking Wallet Balances
Use the npx awal@2.12.0 balance command to fetch token balances across chains. By default it returns balances for USDC + the native gas token on Base, Polygon, and Solana in a single call.
If the wallet is not authenticated, see references/auth.md. The CLI reads the address from the local wallet session — you do not pass an address argument.
Command Syntax
npx awal@2.12.0 balance [--chain <chain>] [--asset <asset>] [--json]Options
| Option | Description |
|---|---|
--chain <chain> | Restrict output to one chain. One of base, base-sepolia, polygon, solana, solana-devnet. Default: all chains. |
--asset <asset> | Show only one asset across the queried chain(s). One of usdc, eth, pol, sol. |
--json | Emit machine-readable JSON instead of the human table. |
-h, --help | Print built-in help. |
If both --chain and --asset are omitted, the command queries every supported mainnet chain (Base, Polygon, Solana) and every native asset on each.
Note on `--asset`: Although the CLI's invalid-asset error message implies a0xcontract address is accepted, passing one currently fails checksum validation (the CLI uppercases the address). In practice, only the symbolic valuesusdc,eth,pol,solwork. Stick to those.
Input Validation
Before constructing the command, validate all user-provided values to prevent shell injection:
- chain: Must be one of
base,base-sepolia,polygon,solana,solana-devnet. Reject any other value. - asset: Must be one of
usdc,eth,pol,sol. Reject any other value. - Reject any value containing spaces, semicolons, pipes, backticks,
$, or other shell metacharacters.
Do not pass unvalidated user input into the command.
Asset / Chain Compatibility
Native gas tokens are chain-specific. Behavior on unsupported asset/chain combinations is inconsistent — see the warnings below.
| Asset | Available on chains |
|---|---|
usdc | base, base-sepolia, polygon, solana, solana-devnet |
eth | base, base-sepolia |
pol | polygon |
sol | solana, solana-devnet |
Token decimals: USDC = 6, ETH = 18, POL = 18, SOL = 9.
Known CLI quirks for --asset on incompatible chains
- `--asset eth` on Solana prints a raw error inline (e.g.
Unsupported Solana asset: "ETH") instead of returning a clean emptybalancesobject. Same for other incompatible symbolic assets on Solana. - `--asset pol` on Base incorrectly returns a non-zero
POLvalue that is actually the wallet's ETH balance (mislabeled). Do not trust `pol` readings on Base — only querypolwith--chain polygon. - `--asset sol` on Base has the same bug — it returns the ETH balance labeled as
SOL. Only query `sol` with `--chain solana` or `--chain solana-devnet`. - Always pair non-USDC assets with their correct chain explicitly (
--asset eth --chain base,--asset pol --chain polygon,--asset sol --chain solana). Do not rely on the CLI to filter cross-chain.
Examples
# Default — all chains, all native assets + USDC
npx awal@2.12.0 balance
# One chain only (mainnet Base)
npx awal@2.12.0 balance --chain base
# Testnet balance (Base Sepolia)
npx awal@2.12.0 balance --chain base-sepolia
# Just USDC, across every chain
npx awal@2.12.0 balance --asset usdc
# Just ETH on Base
npx awal@2.12.0 balance --chain base --asset eth
# Solana SOL balance
npx awal@2.12.0 balance --chain solana --asset sol
# Machine-readable JSON
npx awal@2.12.0 balance --json
npx awal@2.12.0 balance --chain base --asset usdc --jsonOutput Format
Human-readable (default)
Base
────────────────────────
USDC 0.00
ETH 0.00
Polygon
────────────────────────
USDC 0.00
POL 0.00
Solana
────────────────────────
USDC 0.00
SOL 0.00
Tokens from x402 payments
────────────────────────
<SYMBOL> (<network>) <formatted>When --chain and --asset are both omitted, the CLI appends a Tokens from x402 payments section listing arbitrary ERC-20 tokens (by symbol and network) seen during prior x402 pay calls that still hold a non-zero balance. The section is omitted entirely if no such balances exist, or when filtering by --chain or --asset.
Amounts are shown in their human-readable form (e.g. 5.00 USDC, 0.0123 ETH), already converted from atomic units.
JSON (--json)
When --chain is omitted, the response is keyed by chain id (base, polygon, solana, etc.):
{
"base": {
"address": "0x27cCf9aeD0D12890D4507Ee0A5CDd876C9e3DF39",
"chain": "Base",
"balances": {
"USDC": { "raw": "0", "formatted": "0.00", "decimals": 6 },
"ETH": { "raw": "0", "formatted": "0.00", "decimals": 18 }
},
"timestamp": "2026-05-07T14:56:07.571Z"
},
"polygon": { "...": "..." },
"solana": { "...": "..." }
}When --chain <chain> is provided, the response is a single chain object (no top-level chain key):
{
"address": "0x27cCf9aeD0D12890D4507Ee0A5CDd876C9e3DF39",
"chain": "Base",
"balances": {
"USDC": { "raw": "0", "formatted": "0.00", "decimals": 6 },
"ETH": { "raw": "0", "formatted": "0.00", "decimals": 18 }
},
"timestamp": "2026-05-07T14:56:07.571Z"
}JSON field reference for each balance entry:
| Field | Type | Description |
|---|---|---|
raw | string | Atomic units as a decimal string (e.g. "1000000" = 1.00 USDC). Use a big-int parser; the value can exceed Number.MAX_SAFE_INTEGER. |
formatted | string | Human-readable amount, already scaled by decimals. |
decimals | number | Number of decimals for the asset (USDC = 6, ETH/POL = 18, SOL = 9). |
Top-level fields:
| Field | Type | Description |
|---|---|---|
address | string | The wallet address on that chain. EVM (0x…) for Base/Base-Sepolia/Polygon, Base58 for Solana/Solana-Devnet. |
chain | string | Display name of the chain (e.g. "Base", "Base Sepolia"). |
balances | object | Map keyed by uppercase asset symbol (USDC, ETH, POL, SOL). |
timestamp | string | ISO-8601 UTC timestamp of when the balance was read. |
Converting Between Atomic Units and Human-Readable
raw is in atomic units; formatted is the value divided by 10^decimals.
| Asset | Decimals | Atomic example | Human |
|---|---|---|---|
| USDC | 6 | 1000000 | 1.00 |
| USDC | 6 | 100000 | 0.10 |
| ETH | 18 | 1000000000000000 | 0.001 |
| POL | 18 | 1000000000000000000 | 1.00 |
| SOL | 9 | 1000000000 | 1.00 |
When passing --max-amount to x402 pay, or atomic amounts to send/trade, always use the raw field — never formatted.
Common Use Cases
Pre-flight before a send / trade / x402 pay
# Check whether the wallet has enough USDC on Base before paying / sending
npx awal@2.12.0 balance --chain base --asset usdc --jsonIf formatted is below the required amount, see references/fund.md to top up.
Check spendable USDC across all chains
npx awal@2.12.0 balance --asset usdc --jsonConfirm gas (ETH on Base, POL on Polygon) is available before a swap
npx awal@2.12.0 balance --chain base --asset eth --json
npx awal@2.12.0 balance --chain polygon --asset pol --jsonETH/POL are only required when the swap or send is on that chain — most USDC sends/trades on Base are gasless via paymaster, but trades may require small ETH for gas.
Get only the wallet address
If you only need the address (not balances), prefer the cheaper address command:
# Human-readable, all chains
npx awal@2.12.0 address
# Machine-readable, all chains
npx awal@2.12.0 address --json
# Single-chain (returns just the bare address string with no label)
npx awal@2.12.0 address --chain baseOutput shapes — important:
address --json(no--chain) does not return a structured per-chain object. It returns a single object whoseaddressfield is a multi-line string, e.g.:
{ "address": "EVM (Base): 0x27cCf9aeD0D12890D4507Ee0A5CDd876C9e3DF39\nSolana: <base58-address>" }If you need separate EVM and Solana addresses programmatically, prefer balance --json and read the per-chain address field, or split the string on the newline and the EVM (...): / Solana: prefixes.
address --chain <chain>prints just the raw address for that chain with no label or JSON wrapper, even without--json. Useful for shell substitution:ADDR=$(npx awal@2.12.0 address --chain base).
Prerequisites
- Must be authenticated (
npx awal@2.12.0 statusto check; seereferences/auth.md). - Server reachable —
balancecalls the local wallet companion which talks to CDP.
Error Handling
| Symptom | Resolution |
|---|---|
Not authenticated / not signed in | Run the sign-in flow in references/auth.md. |
Hangs on Fetching balances... | The wallet companion may be unreachable. Run npx awal@2.12.0 status to verify server health. |
balances object empty for a chain | The asset filter has no match on that chain (e.g. --asset eth --chain polygon). Drop the filter or use a supported asset. |
Inline Unsupported Solana asset: "ETH" (or similar) printed in output | You passed an incompatible --asset for that chain (e.g. --asset eth --chain solana). Use a supported asset for the chain — see "Asset / Chain Compatibility" above. |
Non-zero POL on Base or SOL on Base | CLI bug: it's reporting the ETH balance under the wrong symbol. Re-query with the correct chain (--asset pol --chain polygon, --asset sol --chain solana). |
Invalid chain | Use one of base, base-sepolia, polygon, solana, solana-devnet. |
Invalid token: "<value>". Must be usdc, eth, pol, or a valid 0x address | Use one of usdc, eth, pol, sol. Despite the message, raw 0x contract addresses currently fail checksum validation — stick to symbolic values. |
Related References
- Top up the wallet:
references/fund.md - Send tokens after confirming sufficient balance:
references/send-usdc.md - Swap tokens:
references/trade.md - Pay an x402 endpoint (uses USDC on Base):
references/x402-pay.md
Funding the Wallet
Use the wallet companion app to fund the wallet with USDC via Coinbase Onramp. This supports multiple payment methods including Apple Pay, debit cards, bank transfers, and funding from a Coinbase account.
If the wallet is not authenticated, see references/auth.md.
Opening the Funding Interface
npx awal@2.12.0 showThis opens the wallet companion window where users can:
1. Select a preset amount ($10, $20, $50) or enter a custom amount 2. Choose their preferred payment method 3. Complete the purchase through Coinbase Pay
Payment Methods
| Method | Description |
|---|---|
| Apple Pay | Fast checkout with Apple Pay (where available) |
| Coinbase | Transfer from existing Coinbase account |
| Card | Debit card payment |
| Bank | ACH bank transfer |
Alternative
You can also ask your human to send USDC on Base to your wallet address. Get the address with:
npx awal@2.12.0 addressPrerequisites
- Must be authenticated (
npx awal@2.12.0 statusto check) - Coinbase Onramp is available in supported regions (US, etc.)
Flow
1. Run npx awal@2.12.0 show to open the wallet UI 2. Instruct the user to click the Fund button 3. User selects amount and payment method in the UI 4. User completes payment through Coinbase Pay (opens in browser) 5. USDC is deposited to the wallet once payment confirms
Checking Balance After Funding
# Check updated balance
npx awal@2.12.0 balanceNotes
- Funding goes through Coinbase's regulated onramp
- Processing time varies by payment method (instant for card/Apple Pay, 1-3 days for bank)
- Funds are deposited as USDC on Base network
- If funding is not available, users can also send USDC on Base directly to the wallet address
Query Onchain Data on Base
Use the CDP SQL API to query onchain data (events, transactions, blocks, transfers) on Base. Queries are executed via x402 and are charged per query.
If the wallet is not authenticated, see references/auth.md.
Executing a Query
npx awal@2.12.0 x402 pay https://x402.cdp.coinbase.com/platform/v2/data/query/run -X POST -d '{"sql": "<YOUR_QUERY>"}' --jsonIMPORTANT: Always single-quote the -d JSON string to prevent bash variable expansion.
Input Validation
Before constructing the command, validate inputs to prevent shell injection:
- SQL query: Always embed the query inside a single-quoted JSON string (
-d '{"sql": "..."}'). Never use double quotes for the outer-dwrapper, as this enables shell expansion of$and backticks within the query. - Addresses: Must be valid
0xhex addresses (^0x[0-9a-fA-F]{40}$). Reject any value containing shell metacharacters.
Do not pass unvalidated user input into the command.
CRITICAL: Indexed Fields
Queries against base.events MUST filter on indexed fields to avoid full table scans. The indexed fields are:
| Indexed Field | Use For |
|---|---|
event_signature | Filter by event type. Use this instead of event_name for performance. |
address | Filter by contract address. |
block_timestamp | Filter by time range. |
Always include at least one indexed field in your WHERE clause. Combining all three gives the best performance.
CoinbaseQL Syntax
CoinbaseQL is a SQL dialect based on ClickHouse. Supported features:
- Clauses: SELECT (DISTINCT), FROM, WHERE, GROUP BY, ORDER BY (ASC/DESC), LIMIT, WITH (CTEs), UNION (ALL/DISTINCT)
- Joins: INNER, LEFT, RIGHT, FULL with ON
- Operators:
=,!=,<>,<,>,<=,>=,+,-,*,/,%, AND, OR, NOT, BETWEEN, IN, IS NULL, LIKE - Expressions: CASE/WHEN/THEN/ELSE, CAST (both
CAST()and::syntax), subqueries, array/map indexing with[], dot notation - Literals: Array
[...], Map{...}, Tuple(...) - Functions: Standard SQL functions, lambda functions with
->syntax
Available Tables
base.events
Decoded event logs from smart contract interactions. This is the primary table for most queries.
| Column | Type | Description |
|---|---|---|
| log_id | String | Unique log identifier |
| block_number | UInt64 | Block number |
| block_hash | FixedString(66) | Block hash |
| block_timestamp | DateTime64(3, 'UTC') | Block timestamp (INDEXED) |
| transaction_hash | FixedString(66) | Transaction hash |
| transaction_to | FixedString(42) | Transaction recipient |
| transaction_from | FixedString(42) | Transaction sender |
| log_index | UInt32 | Log index within block |
| address | FixedString(42) | Contract address (INDEXED) |
| topics | Array(FixedString(66)) | Event topics |
| event_name | LowCardinality(String) | Decoded event name |
| event_signature | LowCardinality(String) | Event signature (INDEXED - prefer over event_name) |
| parameters | Map(String, Variant(Bool, Int256, String, UInt256)) | Decoded event parameters |
| parameter_types | Map(String, String) | ABI types for parameters |
| action | Enum8('removed' = -1, 'added' = 1) | Added or removed (reorg) |
base.transactions
Complete transaction data.
| Column | Type | Description |
|---|---|---|
| block_number | UInt64 | Block number |
| block_hash | String | Block hash |
| transaction_hash | String | Transaction hash |
| transaction_index | UInt64 | Index in block |
| from_address | String | Sender address |
| to_address | String | Recipient address |
| value | String | Value transferred (wei) |
| gas | UInt64 | Gas limit |
| gas_price | UInt64 | Gas price |
| input | String | Input data |
| nonce | UInt64 | Sender nonce |
| type | UInt64 | Transaction type |
| max_fee_per_gas | UInt64 | EIP-1559 max fee |
| max_priority_fee_per_gas | UInt64 | EIP-1559 priority fee |
| chain_id | UInt64 | Chain ID |
| v | String | Signature v |
| r | String | Signature r |
| s | String | Signature s |
| is_system_tx | Bool | System transaction flag |
| max_fee_per_blob_gas | String | Blob gas fee |
| blob_versioned_hashes | Array(String) | Blob hashes |
| timestamp | DateTime | Block timestamp |
| action | Int8 | Added (1) or removed (-1) |
base.blocks
Block-level metadata.
| Column | Type | Description |
|---|---|---|
| block_number | UInt64 | Block number |
| block_hash | String | Block hash |
| parent_hash | String | Parent block hash |
| timestamp | DateTime | Block timestamp |
| miner | String | Block producer |
| nonce | UInt64 | Block nonce |
| sha3_uncles | String | Uncles hash |
| transactions_root | String | Transactions merkle root |
| state_root | String | State merkle root |
| receipts_root | String | Receipts merkle root |
| logs_bloom | String | Bloom filter |
| gas_limit | UInt64 | Block gas limit |
| gas_used | UInt64 | Gas used in block |
| base_fee_per_gas | UInt64 | Base fee per gas |
| total_difficulty | String | Total chain difficulty |
| size | UInt64 | Block size in bytes |
| extra_data | String | Extra data field |
| mix_hash | String | Mix hash |
| withdrawals_root | String | Withdrawals root |
| parent_beacon_block_root | String | Beacon chain parent root |
| blob_gas_used | UInt64 | Blob gas used |
| excess_blob_gas | UInt64 | Excess blob gas |
| transaction_count | UInt64 | Number of transactions |
| action | Int8 | Added (1) or removed (-1) |
Example Queries
Get recent USDC Transfer events with decoded parameters
SELECT
parameters['from'] AS sender,
parameters['to'] AS to,
parameters['value'] AS amount,
address AS token_address
FROM base.events
WHERE
event_signature = 'Transfer(address,address,uint256)'
AND address = '0x833589fcd6edb6e08f4c7c32d4f71b54bda02913'
AND block_timestamp >= now() - INTERVAL 7 DAY
LIMIT 10Get transactions from a specific address
npx awal@2.12.0 x402 pay https://x402.cdp.coinbase.com/platform/v2/data/query/run -X POST -d '{"sql": "SELECT transaction_hash, to_address, value, gas, timestamp FROM base.transactions WHERE from_address = lower('\''0xYOUR_ADDRESS'\'') AND timestamp >= now() - INTERVAL 1 DAY LIMIT 10"}' --jsonCount events by type for a contract in the last hour
npx awal@2.12.0 x402 pay https://x402.cdp.coinbase.com/platform/v2/data/query/run -X POST -d '{"sql": "SELECT event_signature, count(*) as cnt FROM base.events WHERE address = lower('\''0xCONTRACT_ADDRESS'\'') AND block_timestamp >= now() - INTERVAL 1 HOUR GROUP BY event_signature ORDER BY cnt DESC LIMIT 20"}' --jsonGet latest block info
npx awal@2.12.0 x402 pay https://x402.cdp.coinbase.com/platform/v2/data/query/run -X POST -d '{"sql": "SELECT block_number, timestamp, transaction_count, gas_used FROM base.blocks ORDER BY block_number DESC LIMIT 1"}' --jsonCommon Contract Addresses (Base)
| Token | Address |
|---|---|
| USDC | 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 |
| WETH | 0x4200000000000000000000000000000000000006 |
Best Practices
1. Always filter on indexed fields (event_signature, address, block_timestamp) in base.events queries. 2. *Never use `SELECT ** - specify only the columns you need. 3. **Always include a LIMIT** clause to bound result size. 4. **Use event_signature instead of event_name** for filtering - it is indexed and much faster. 5. **Use time-bounded queries** with block_timestamp to narrow the scan range. 6. **Always wrap address values in lower()** - the database stores lowercase addresses but users may provide checksummed (mixed-case) addresses. Use address = lower('0xAbC...') not address = '0xAbC...'. 7. **Common event signatures**: Transfer(address,address,uint256), Approval(address,address,uint256), Swap(address,uint256,uint256,uint256,uint256,address)`.
Prerequisites
- Must be authenticated (
npx awal@2.12.0 statusto check; seereferences/auth.md) - Wallet must have sufficient USDC balance (
npx awal@2.12.0 balanceto check; seereferences/fund.md) - Each query costs $0.10 (100000 USDC atomic units)
Error Handling
- "Not authenticated" - See
references/auth.md - "Insufficient balance" - See
references/fund.md - Query timeout or error - Ensure you are filtering on indexed fields and using a LIMIT
Sending Tokens
Use the npx awal@2.12.0 send command to transfer tokens from the wallet to any address on Base, Polygon, or Solana.
If the wallet is not authenticated, see references/auth.md.
Command Syntax
npx awal@2.12.0 send <amount> <recipient> [--chain <chain>] [--asset <asset>] [--json]Arguments
| Argument | Description |
|---|---|
amount | Amount to send: '$1.00', '1.00', or atomic units (1000000 = $1). Always single-quote amounts that use $ to prevent bash variable expansion. If the number looks like atomic units (no decimal or > 100), treat as atomic units. Assume that people won't be sending more than 100 USDC the majority of the time |
recipient | Ethereum address (0x...), ENS name (vitalik.eth), or Solana address (Base58) |
Options
| Option | Description |
|---|---|
--chain <name> | Blockchain network: base, polygon, solana (default: base) |
--asset <symbol> | Token to send: usdc, eth, pol, sol (default: usdc) |
--json | Output result as JSON |
Input Validation
Before constructing the command, validate all user-provided values to prevent shell injection:
- amount: Must match
^\$?[\d.]+$(digits, optional decimal point, optional$prefix). Reject if it contains spaces, semicolons, pipes, backticks, or other shell metacharacters. - recipient: Must be a valid
0xhex address (^0x[0-9a-fA-F]{40}$), an ENS name (^[a-zA-Z0-9.-]+\.eth$), or a Solana address (^[1-9A-HJ-NP-Za-km-z]{32,44}$). Reject any value containing spaces or shell metacharacters. - chain: Must be one of
base,polygon,solana. Reject any other value. - asset: Must be one of
usdc,eth,pol,sol. Reject any other value.
Do not pass unvalidated user input into the command.
Examples
# Send $1.00 USDC to an address on Base (default)
npx awal@2.12.0 send 1 0x1234...abcd
# Send $0.50 USDC to an ENS name
npx awal@2.12.0 send 0.50 vitalik.eth
# Send with dollar sign prefix (note the single quotes)
npx awal@2.12.0 send '$5.00' 0x1234...abcd
# Send ETH on Base
npx awal@2.12.0 send 0.01 0x1234...abcd --asset eth
# Send USDC on Polygon
npx awal@2.12.0 send 1 0x1234...abcd --chain polygon
# Send USDC to a Solana address
npx awal@2.12.0 send 1 AxW7...5fGz --chain solana
# Get JSON output
npx awal@2.12.0 send 1 vitalik.eth --jsonENS Resolution
ENS names are automatically resolved to addresses via Ethereum mainnet. The command will:
1. Detect ENS names (any string containing a dot that isn't a hex address) 2. Resolve the name to an address 3. Display both the ENS name and resolved address in the output
Prerequisites
- Must be authenticated (
npx awal@2.12.0 statusto check; seereferences/auth.md) - Wallet must have sufficient balance (
npx awal@2.12.0 balanceto check; seereferences/fund.mdto top up)
Error Handling
Common errors:
- "Not authenticated" - See
references/auth.md - "Insufficient balance" - Check balance with
npx awal@2.12.0 balance; seereferences/fund.md - "Could not resolve ENS name" - Verify the ENS name exists
- "Invalid recipient" - Must be valid 0x address, ENS name, or Solana Base58 address
- "SOL only supported on Solana chains" - Use
--chain solanawhen sending SOL - "ETH/POL only supported on EVM chains" - ETH on base, POL on polygon
Trading Tokens
Use the npx awal@2.12.0 trade command to swap tokens on Base or Polygon via the CDP Swap API. You must be authenticated to trade.
If the wallet is not authenticated, see references/auth.md.
Command Syntax
npx awal@2.12.0 trade <amount> <from> <to> [options]The command is also available as npx awal@2.12.0 swap (alias).
Arguments
| Argument | Description |
|---|---|
amount | Amount to swap (see Amount Formats below) |
from | Source token: alias (usdc, eth, pol) or contract address (0x...) |
to | Destination token: alias (usdc, eth, pol) or contract address (0x...) |
Amount Formats
The amount can be specified in multiple formats:
| Format | Example | Description |
|---|---|---|
| Dollar prefix | '$1.00', '$0.50' | USD notation (decimals based on token) |
| Decimal | 1.0, 0.50, 0.001 | Human-readable with decimal point |
| Whole number | 5, 100 | Interpreted as whole tokens |
| Atomic units | 500000 | Large integers treated as atomic units |
Auto-detection: Large integers without a decimal point are treated as atomic units. For example, 500000 for USDC (6 decimals) = $0.50.
Decimals: For known tokens (usdc=6, eth=18, pol=18), decimals are automatic. For arbitrary contract addresses, decimals are read from the token contract.
Options
| Option | Description |
|---|---|
-c, --chain <name> | Blockchain network: base, polygon (default: base) |
-s, --slippage <n> | Slippage tolerance in basis points (100 = 1%) |
--json | Output result as JSON |
Token Aliases
| Alias | Token | Decimals | Chain |
|---|---|---|---|
| usdc | USDC | 6 | base |
| eth | ETH | 18 | base |
| pol | POL | 18 | polygon |
IMPORTANT: Always single-quote amounts that use $ to prevent bash variable expansion (e.g. '$1.00' not $1.00).
Input Validation
Before constructing the command, validate all user-provided values to prevent shell injection:
- amount: Must match
^\$?[\d.]+$(digits, optional decimal point, optional$prefix). Reject if it contains spaces, semicolons, pipes, backticks, or other shell metacharacters. - from / to: Must be a known alias (
usdc,eth,pol) or a valid0xhex address (^0x[0-9a-fA-F]{40}$). Reject any other value. - slippage: Must be a positive integer (
^\d+$).
Do not pass unvalidated user input into the command.
Examples
# Swap $1 USDC for ETH (dollar prefix — note the single quotes)
npx awal@2.12.0 trade '$1' usdc eth
# Swap 0.50 USDC for ETH (decimal format)
npx awal@2.12.0 trade 0.50 usdc eth
# Swap 500000 atomic units of USDC for ETH
npx awal@2.12.0 trade 500000 usdc eth
# Swap 0.01 ETH for USDC
npx awal@2.12.0 trade 0.01 eth usdc
# Swap with custom slippage (2%)
npx awal@2.12.0 trade '$5' usdc eth --slippage 200
# Swap using contract addresses (decimals read from chain)
npx awal@2.12.0 trade 100 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913 0x4200000000000000000000000000000000000006
# Get JSON output
npx awal@2.12.0 trade '$1' usdc eth --json
# Swap USDC for POL on Polygon
npx awal@2.12.0 trade '$1' usdc pol --chain polygonPrerequisites
- Must be authenticated (
npx awal@2.12.0 statusto check; seereferences/auth.md) - Wallet must have sufficient balance of the source token
Error Handling
Common errors:
- "Not authenticated" - See
references/auth.md - "Invalid token" - Use a valid alias (usdc, eth, pol) or 0x address
- "POL only supported on polygon chain" - Use
--chain polygonwhen trading POL - "Cannot swap a token to itself" - From and to must be different
- "Swap failed: TRANSFER_FROM_FAILED" - Insufficient balance or approval issue
- "No liquidity" - Try a smaller amount or different token pair
- "Amount has X decimals but token only supports Y" - Too many decimal places
Build an x402 Payment Server
Create an Express server that charges USDC for API access using the x402 payment protocol. Callers pay per-request in USDC on Base — no accounts, API keys, or subscriptions needed. Your service is automatically discoverable by other agents via the x402 Bazaar.
How It Works
x402 is an HTTP-native payment protocol. When a client hits a protected endpoint without paying, the server returns HTTP 402 with payment requirements. The client signs a USDC payment and retries with a payment header. The facilitator verifies and settles the payment, and the server returns the response. Services register with the x402 Bazaar so other agents can discover and pay for them automatically.
If the wallet is not authenticated, see references/auth.md.
Step 1: Get the Payment Address
Run this to get the wallet address that will receive payments:
npx awal@2.12.0 addressUse this address as the payTo value.
Step 2: Set Up the Project
mkdir x402-server && cd x402-server
npm init -y
npm install express @x402/express @x402/core @x402/evm @x402/extensionsCreate index.js:
const express = require("express");
const { paymentMiddleware } = require("@x402/express");
const { x402ResourceServer, HTTPFacilitatorClient } = require("@x402/core/server");
const { ExactEvmScheme } = require("@x402/evm/exact/server");
const app = express();
app.use(express.json());
const PAY_TO = "<address from step 1>";
// Create facilitator client and x402 resource server
const facilitator = new HTTPFacilitatorClient({ url: "https://x402.org/facilitator" });
const server = new x402ResourceServer(facilitator);
server.register("eip155:8453", new ExactEvmScheme());
// x402 payment middleware — protects routes below
app.use(
paymentMiddleware(
{
"GET /api/example": {
accepts: {
scheme: "exact",
price: "$0.01",
network: "eip155:8453",
payTo: PAY_TO,
},
description: "Description of what this endpoint returns",
mimeType: "application/json",
},
},
server,
),
);
// Protected endpoint
app.get("/api/example", (req, res) => {
res.json({ data: "This costs $0.01 per request" });
});
app.listen(3000, () => console.log("Server running on port 3000"));Step 3: Run It
node index.jsTest with curl — you should get a 402 response with payment requirements:
curl -i http://localhost:3000/api/exampleAPI Reference
paymentMiddleware(routes, server)
Creates Express middleware that enforces x402 payments.
| Parameter | Type | Description |
|---|---|---|
routes | object | Route config mapping route patterns to payment config |
server | x402ResourceServer | Pre-configured x402 resource server instance |
x402ResourceServer
Created with a facilitator client. Register payment schemes and extensions before passing to middleware.
const { x402ResourceServer, HTTPFacilitatorClient } = require("@x402/core/server");
const { ExactEvmScheme } = require("@x402/evm/exact/server");
const facilitator = new HTTPFacilitatorClient({ url: "https://x402.org" });
const server = new x402ResourceServer(facilitator);
server.register("eip155:8453", new ExactEvmScheme());| Method | Description |
|---|---|
register(network, scheme) | Register a payment scheme for a CAIP-2 network identifier |
Route Config
Each key in the routes object is "METHOD /path". The value is a config object:
{
"GET /api/data": {
accepts: {
scheme: "exact",
price: "$0.05",
network: "eip155:8453",
payTo: "0x...",
},
description: "Human-readable description of the endpoint",
mimeType: "application/json",
extensions: {
...declareDiscoveryExtension({
output: {
example: { result: "example response" },
schema: {
properties: {
result: { type: "string" },
},
},
},
}),
},
},
}Accepts Config Fields
The accepts field can be a single object or an array (for multiple payment options):
| Field | Type | Description |
|---|---|---|
scheme | string | Payment scheme: "exact" |
price | string | USDC price (e.g. "$0.01", "$1.00") |
network | string | CAIP-2 network identifier (e.g. "eip155:8453") |
payTo | string | Ethereum address (0x...) to receive USDC payments |
Route-Level Fields
| Field | Type | Description |
|---|---|---|
accepts | object or array | Payment requirements (single or multiple) |
description | string? | What this endpoint does (shown to clients) |
mimeType | string? | MIME type of the response |
extensions | object? | Extensions config (e.g. Bazaar discovery) |
Discovery Extension
The declareDiscoveryExtension function registers your endpoint with the x402 Bazaar so other agents can discover it:
const { declareDiscoveryExtension } = require("@x402/extensions/bazaar");
extensions: {
...declareDiscoveryExtension({
output: {
example: { /* example response body */ },
schema: {
properties: {
/* JSON schema of the response */
},
},
},
}),
}| Field | Type | Description |
|---|---|---|
output.example | object | Example response body for the endpoint |
output.schema | object | JSON schema describing the response format |
Supported Networks
| Network | Description |
|---|---|
eip155:8453 | Base mainnet (real USDC) |
eip155:84532 | Base Sepolia testnet (test USDC) |
Patterns
Multiple endpoints with different prices
app.use(
paymentMiddleware(
{
"GET /api/cheap": {
accepts: {
scheme: "exact",
price: "$0.001",
network: "eip155:8453",
payTo: PAY_TO,
},
description: "Inexpensive data lookup",
},
"GET /api/expensive": {
accepts: {
scheme: "exact",
price: "$1.00",
network: "eip155:8453",
payTo: PAY_TO,
},
description: "Premium data access",
},
"POST /api/query": {
accepts: {
scheme: "exact",
price: "$0.25",
network: "eip155:8453",
payTo: PAY_TO,
},
description: "Run a custom query",
},
},
server,
),
);
app.get("/api/cheap", (req, res) => { /* ... */ });
app.get("/api/expensive", (req, res) => { /* ... */ });
app.post("/api/query", (req, res) => { /* ... */ });Wildcard routes
app.use(
paymentMiddleware(
{
"GET /api/*": {
accepts: {
scheme: "exact",
price: "$0.05",
network: "eip155:8453",
payTo: PAY_TO,
},
description: "API access",
},
},
server,
),
);
app.get("/api/users", (req, res) => { /* ... */ });
app.get("/api/posts", (req, res) => { /* ... */ });Health check (no payment)
Register free endpoints before the payment middleware:
app.get("/health", (req, res) => res.json({ status: "ok" }));
// Payment middleware only applies to routes registered after it
app.use(paymentMiddleware({ /* ... */ }, server));
app.get("/api/data", (req, res) => { /* ... */ });POST with body and discovery extension
app.use(
paymentMiddleware(
{
"POST /api/analyze": {
accepts: {
scheme: "exact",
price: "$0.10",
network: "eip155:8453",
payTo: PAY_TO,
},
description: "Analyze text sentiment",
mimeType: "application/json",
extensions: {
...declareDiscoveryExtension({
output: {
example: { sentiment: "positive", score: 0.95 },
schema: {
properties: {
sentiment: { type: "string" },
score: { type: "number" },
},
},
},
}),
},
},
},
server,
),
);
app.post("/api/analyze", (req, res) => {
const { text } = req.body;
// ... your logic
res.json({ sentiment: "positive", score: 0.95 });
});Multiple payment options per endpoint
Accept payments on multiple networks for the same endpoint:
"GET /api/data": {
accepts: [
{
scheme: "exact",
price: "$0.01",
network: "eip155:8453",
payTo: EVM_ADDRESS,
},
{
scheme: "exact",
price: "$0.01",
network: "eip155:84532",
payTo: EVM_ADDRESS,
},
],
description: "Data endpoint accepting Base mainnet or testnet",
}Using the CDP facilitator (authenticated)
For production use with the Coinbase facilitator (supports mainnet):
npm install @coinbase/x402const { facilitator } = require("@coinbase/x402");
const { HTTPFacilitatorClient } = require("@x402/core/server");
const facilitatorClient = new HTTPFacilitatorClient(facilitator);
const server = new x402ResourceServer(facilitatorClient);
server.register("eip155:8453", new ExactEvmScheme());This requires CDP_API_KEY_ID and CDP_API_KEY_SECRET environment variables. Get these from https://portal.cdp.coinbase.com.
Testing the Server
Once the server is running, test payments using the commands in references/x402-pay.md:
# Check the endpoint's payment requirements
npx awal@2.12.0 x402 details http://localhost:3000/api/example
# Make a paid request
npx awal@2.12.0 x402 pay http://localhost:3000/api/examplePricing Guidelines
| Use Case | Suggested Price |
|---|---|
| Simple data lookup | $0.001 - $0.01 |
| API proxy / enrichment | $0.01 - $0.10 |
| Compute-heavy query | $0.10 - $0.50 |
| AI inference | $0.05 - $1.00 |
Checklist
- [ ] Get wallet address with
npx awal@2.12.0 address - [ ] Install
express,@x402/express,@x402/core,@x402/evm, and@x402/extensions - [ ] Create
x402ResourceServerwith facilitator client and registerExactEvmSchemeforeip155:8453 - [ ] Define routes with prices, descriptions, and discovery extensions (Bazaar auto-registers when routes declare it)
- [ ] Register payment middleware before protected routes
- [ ] Keep health/status endpoints before payment middleware
- [ ] Test with
curl(should get 402) andnpx awal@2.12.0 x402 pay(should get 200) - [ ] Announce your service so other agents can find and use it
Making Paid x402 Requests
Use the npx awal@2.12.0 x402 pay command to call paid API endpoints with automatic USDC payment on Base.
If the wallet is not authenticated, see references/auth.md.
Command Syntax
npx awal@2.12.0 x402 pay <url> [-X <method>] [-d <json>] [-q <params>] [-h <json>] [--max-amount <n>] [--json]Options
| Option | Description |
|---|---|
-X, --method <method> | HTTP method (default: GET) |
-d, --data <json> | Request body as JSON string |
-q, --query <params> | Query parameters as JSON string |
-h, --headers <json> | Custom HTTP headers as JSON string |
--max-amount <amount> | Max payment in USDC atomic units (1000000 = $1.00) |
--correlation-id <id> | Group related operations |
--json | Output as JSON |
USDC Amounts
X402 uses USDC atomic units (6 decimals):
| Atomic Units | USD |
|---|---|
| 1000000 | $1.00 |
| 100000 | $0.10 |
| 50000 | $0.05 |
| 10000 | $0.01 |
IMPORTANT: Always single-quote amounts that use $ to prevent bash variable expansion (e.g. '$1.00' not $1.00).
Input Validation
Before constructing the command, validate all user-provided values to prevent shell injection:
- url: Must be a valid URL starting with
https://orhttp://. Reject if it contains spaces, semicolons, pipes, backticks, or shell metacharacters. - data (-d): Must be valid JSON. Always wrap in single quotes to prevent shell expansion.
- max-amount: Must be a positive integer (
^\d+$).
Do not pass unvalidated user input into the command.
Examples
# Make a GET request (auto-pays)
npx awal@2.12.0 x402 pay https://example.com/api/weather
# Make a POST request with body
npx awal@2.12.0 x402 pay https://example.com/api/sentiment -X POST -d '{"text": "I love this product"}'
# Limit max payment to $0.10
npx awal@2.12.0 x402 pay https://example.com/api/data --max-amount 100000Prerequisites
- Must be authenticated (
npx awal@2.12.0 statusto check; seereferences/auth.md) - Wallet must have sufficient USDC balance (
npx awal@2.12.0 balanceto check; seereferences/fund.mdto top up) - If you don't know the endpoint URL, see
references/x402-search.mdto find services first
Error Handling
- "Not authenticated" - See
references/auth.md - "No X402 payment requirements found" - URL may not be an x402 endpoint; see
references/x402-search.mdto find valid endpoints - "Insufficient balance" - See
references/fund.md
Searching the x402 Bazaar
Use the npx awal@2.12.0 x402 commands to discover and inspect paid API endpoints available on the x402 bazaar marketplace. No authentication or balance is required for searching.
Commands
Search the Bazaar
Find paid services by keyword using CDP's vector search:
npx awal@2.12.0 x402 bazaar search <query> [-k <n>] [--network <network>] [--scheme <scheme>] [--max-price <price>] [--json]| Option | Description |
|---|---|
-k, --top <n> | Number of results, 1–20 (default: 20) |
--network <name> | Filter by chain (base, base-sepolia, polygon, solana, solana-devnet) |
--scheme <scheme> | Filter by payment scheme: exact or upto |
--max-price <price> | Maximum price in USD (e.g. 0.01) |
--asset <address> | Filter by payment asset address |
--pay-to <address> | Filter by recipient wallet address |
--extensions <type> | Filter by extension type (e.g. outputSchema, bazaar) |
--json | Output as JSON |
List Bazaar Resources
Browse all available resources:
npx awal@2.12.0 x402 bazaar list [--network <network>] [--full] [--refresh] [--json]| Option | Description |
|---|---|
--network <name> | Filter by chain (base, base-sepolia, polygon, solana, solana-devnet) |
--full | Show complete details including schemas |
--refresh | Re-fetch resource index from CDP API |
--json | Output as JSON |
Discover Payment Requirements
Inspect an endpoint's x402 payment requirements without paying:
npx awal@2.12.0 x402 details <url> [--json]Auto-detects the correct HTTP method (GET, POST, PUT, DELETE, PATCH) by trying each until it gets a 402 response, then displays price, accepted payment schemes, network, and input/output schemas.
Examples
# Search for weather-related paid APIs
npx awal@2.12.0 x402 bazaar search "weather"
# Search with more results
npx awal@2.12.0 x402 bazaar search "sentiment analysis" -k 10
# Browse all bazaar resources with full details
npx awal@2.12.0 x402 bazaar list --full
# Check what an endpoint costs
npx awal@2.12.0 x402 details https://example.com/api/weatherNext Steps
Once you've found a service you want to use, see references/x402-pay.md to make a paid request to the endpoint.
Error Handling
- "CDP API returned 429" - Rate limited; cached data will be used if available
- "No X402 payment requirements found" - URL may not be an x402 endpoint
Related skills
How it compares
Choose Agentic Wallet for agent-native awal and x402 flows; use a full wallet SDK when building custom non-agent wallet UIs.
FAQ
What tokens does Agentic Wallet support?
Agentic Wallet supports USDC, ETH, POL, and SOL operations through the awal CLI, including sends, balance checks, swaps, and funding. ENS names can be resolved when transferring or querying addresses on supported networks.
What is x402 in Agentic Wallet?
Agentic Wallet uses the x402 payment protocol so agents can discover paid services in the x402 bazaar, pay per API call, or monetize their own endpoints. Payments run onchain on Base without custom payment SDK wiring.