Pay-as-you-go crypto micropayments for CLI tools. Charge per invocation on Base L2 with USDC (via x402) or direct ETH transfers.
Tool developers add a payg.toml to their project and call payg::charge() (Rust) or shell out to payg charge (any language).
End users run payg init once to create a wallet, fund it with USDC on Base, and every tool invocation automatically pays the developer.
Payments go peer-to-peer on Base L2. Gas costs $0.001 or less per transaction.
cargo install payg-clicargo add paygRequires Rust 1.88.0+.
# Create a wallet (defaults to Base Sepolia testnet)
payg init
# Or target mainnet directly
payg init --network base
# Fund the address with USDC on Base (shown after init)
# For testnet USDC: https://faucet.circle.com/
# Then use any PAYG-enabled tool — payments happen automaticallyAdd billing to your CLI in 3 lines:
// In your tool's main function
payg::charge("0.001 USDC", "0xYOUR_WALLET_ADDRESS").await?;Create a payg.toml in your project root:
recipient = "0xYOUR_WALLET_ADDRESS"
default_price = "0.001 USDC"Shell out to the payg binary:
payg charge "0.001 USDC" --recipient 0xYOUR_WALLET_ADDRESSOr rely on payg.toml defaults:
payg chargeAll commands accept these flags:
| Flag | Env var | Default | Description |
|---|---|---|---|
--output |
PAYG_OUTPUT |
text |
Output format: text, json |
--network |
PAYG_NETWORK |
base-sepolia |
Target network: base, base-sepolia |
Precedence: CLI flag > env var > config file > default.
Create a new wallet at ~/.payg/keyfile.json.
payg init # Interactive (prompts for password)
payg init --network base # Create wallet targeting mainnet
payg init --non-interactive # Requires PAYG_KEY_PASSWORD env var
payg init --non-interactive --output jsonSend a payment.
payg charge "0.001 USDC" --recipient 0x1234...
payg charge # Uses payg.toml defaults
payg charge --network base # Charge on mainnet
payg charge --dry-run # Validate without sending
payg charge --dry-run --output json # Machine-readable validationShow wallet address.
payg address
payg address --output json # {"address": "0x..."}Check ETH and USDC balances.
payg balance
payg balance --network base # Check mainnet balances
payg balance --output jsonCreated by tool developers in the project root:
recipient = "0xYOUR_WALLET_ADDRESS"
default_price = "0.001 USDC"| Field | Required | Description |
|---|---|---|
recipient |
Yes | Wallet address to receive payments |
default_price |
No | Default charge amount (e.g. "0.001 USDC") |
Created automatically by payg init:
keyfile = "~/.payg/keyfile.json"
max_charge = "1.00 USDC"
network = "base-sepolia"| Field | Default | Description |
|---|---|---|
keyfile |
~/.payg/keyfile.json |
Path to encrypted keyfile |
max_charge |
1.00 USDC |
Safety ceiling per charge |
network |
base-sepolia |
Default network (base, base-sepolia) |
facilitator_url |
https://x402.org/facilitator |
x402 facilitator endpoint |
rpc_url |
Per-network default | Base RPC endpoint override |
All config values can be overridden with environment variables:
| Variable | Description |
|---|---|
PAYG_PRIVATE_KEY |
Hex private key (skips keyfile, recommended for agents) |
PAYG_KEY_PASSWORD |
Keyfile password (skips interactive prompt) |
PAYG_NETWORK |
Target network: base, base-sepolia (same as --network) |
PAYG_OUTPUT |
Output format: text, json (same as --output) |
PAYG_MAX_CHARGE |
Safety ceiling override (e.g. "5.00 USDC") |
PAYG_RPC_URL |
Base RPC endpoint override |
PAYG_FACILITATOR_URL |
x402 facilitator URL override |
Precedence: CLI flag > env var > config file > default.
For CI/agents, set PAYG_PRIVATE_KEY to avoid keyfile decryption overhead (200-500ms scrypt vs 10-30ms env var).
use payg::{charge, charge_with_config, ConsumerConfig, NetworkConfig, PaygError};
// Simple: loads config and wallet automatically
payg::charge("0.001 USDC", "0xRECIPIENT").await?;
// Pre-loaded: avoids redundant config/wallet loading in loops
let config = ConsumerConfig::load()?;
let network = config.resolve_network()?;
let signer = payg::wallet::load_wallet(&config, Some("password"))?;
let receipt = payg::charge_with_config(
"0.001 USDC",
"0xRECIPIENT",
&config,
network,
signer,
).await?;
println!("tx: {}", receipt.tx_hash);| Format | Example | Result |
|---|---|---|
| USDC | "0.001 USDC" |
1000 units (6 decimals) |
| ETH | "0.01 ETH" |
10^16 wei (18 decimals) |
| Free | "free" |
0 |
[dependencies]
payg = "0.1" # Default: x402 (USDC) backend
payg = { version = "0.1", features = ["eth"] } # Add ETH backend| Feature | Default | Backend |
|---|---|---|
x402 |
Yes | USDC via ERC-3009 + x402 facilitator |
eth |
No | Direct ETH transfer via alloy |
| Code | Meaning |
|---|---|
| 0 | Success |
| 1 | General error |
| 2 | Invalid arguments |
| 42 | Payment failed or exceeds safety ceiling |
| 77 | Wallet error (missing or decryption failure) |
| 78 | Configuration error |
PAYG is designed for agent-native usage:
--output json(orPAYG_OUTPUT=json) on every command for structured output- All JSON responses include
status,network, andis_testnetfields - All errors include a
codefield (NO_WALLET,EXCEEDS_CEILING,INVALID_ARGS, etc.) PAYG_PRIVATE_KEYenv var for fast, non-interactive wallet accessPAYG_NETWORKenv var to select network without CLI flags--dry-runto validate charges before sending- Semantic exit codes for programmatic error handling
# Agent workflow
export PAYG_PRIVATE_KEY=0x...
export PAYG_OUTPUT=json
export PAYG_NETWORK=base-sepolia
payg balance
payg charge "0.001 USDC" --recipient 0x... --dry-run
payg charge "0.001 USDC" --recipient 0x...- Keyfiles encrypted with scrypt, stored with
0600permissions ~/.payg/directory created with0700permissions- Private keys zeroized from memory after use
- URLs validated to require HTTPS (localhost exception for development)
- Safety ceiling checked before wallet load (fail-fast)
- ERC-3009 authorization timeout capped at 120 seconds
Dual-licensed under MIT or Apache-2.0, at your option.