@parallel-protocol/cli
v0.5.1
Published
Command-line interface for the [Parallel Protocol](https://parallel.best/) — query live on-chain data, build and sign transactions, and interact with the x402 payment layer directly from your terminal.
Readme
@parallel-protocol/cli
Command-line interface for the Parallel Protocol — query live on-chain data, build and sign transactions, and interact with the x402 payment layer directly from your terminal.
Installation
# Install globally
npm install -g @parallel-protocol/cli
# Or run without installing
npx @parallel-protocol/cli <command>Requirements: Node.js ≥ 22
Quick start
# 1. Configure (interactive wizard — runs once)
parallel config init
# 2. Explore
parallel protocol overview
parallel protocol tvl --chain base
parallel protocol savings-rate
# 3. Build a transaction (dry-run — no wallet needed)
parallel swap quote --op mint --collateral USDC --amount 1000 --chain avalanche
parallel swap mint --collateral USDC --amount 1000 --chain avalanche --dry-runDevelopment (from the parallel-mcp repo):
bun run build:cli # build once alias parallel="node packages/cli/dist/index.js" parallel protocol overview
Configuration
parallel config init
Interactive wizard that creates ~/.parallel/config.toml:
[network]
default_chain = "base"
[wallet]
keystore_path = "~/.parallel/keystore.json"parallel config get / set
parallel config get network.default_chain # → base
parallel config set network.default_chain ethereumCreating a keystore
Always use a dedicated wallet for testing — never your main wallet.
The CLI uses the Ethereum V3 keystore format (AES-128-CTR encrypted, password required at each use). Three ways to create one:
Option A — Foundry cast (recommended for developers)
mkdir -p ~/.parallel
cast wallet new ~/.parallel
# → generates a fresh address, encrypted with a password you choose,
# as ~/.parallel/<id> (random filename — rename it if you like)Option B — Geth
geth account new --keystore ~/.parallel/Option C — From MetaMask (non-dev users)
# 1. MetaMask → Account details → Export Private Key
# 2. Convert to keystore:
cast wallet import parallel-cli --keystore-dir ~/.parallel --interactive
# → paste the private key, set a password — writes ~/.parallel/parallel-cliThen point the CLI to it:
parallel config set wallet.keystore_path ~/.parallel/parallel-cli
# or pass it directly: --wallet ~/.parallel/parallel-cliEnvironment variables
All optional.
| Variable | Description |
|---|---|
| PARALLEL_PRIVATE_KEY | Raw private key (hex, 0x prefix) — alternative to keystore, useful for scripts and CI. Used by --wallet env, or by --wallet alone when no keystore is configured |
| PARALLEL_DEFAULT_CHAIN | Default chain — overrides network.default_chain in config |
| PARALLEL_JSON | Set to true to force JSON output on all commands |
| PARALLEL_API_URL | Parallel API base URL for --history commands (default: https://api.parallel.best) |
| FACILITATOR_URL | Facilitator base URL for pay commands (default: https://agents.parallel.best) |
| ALCHEMY_API_KEY | Alchemy key, recommended for payments. On the chains Alchemy serves, chain reads go to Alchemy first, before the public endpoints, which limit their rate |
Precedence: flags > environment > config file — except the wallet:
--walletused alone tries the configkeystore_pathfirst, then falls back toPARALLEL_PRIVATE_KEY.
Global flags
These flags are available across most commands. Command-specific flags (e.g. --collateral, --amount, --op) are documented in each command's section.
| Flag | Alias | Description |
|---|---|---|
| --chain <chain> | -c | Target chain (e.g. base, ethereum, avalanche) |
| --json | -j | Force JSON output |
| --dry-run | | Build transaction without broadcasting (write commands only) |
| --wallet [path\|env] | -w | Sign with a wallet — see below |
| --help | -h | Contextual help |
--wallet resolution:
| Usage | Behavior |
|---|---|
| --wallet (alone) | Auto-resolves: reads wallet.keystore_path from config first, then falls back to $PARALLEL_PRIVATE_KEY |
| --wallet env | Explicitly uses $PARALLEL_PRIVATE_KEY |
| --wallet /path/to/keystore.json | Uses that specific keystore file (password prompted) |
Commands
parallel protocol
Read-only. No wallet required.
parallel protocol overview # TVL · supply · APY · backing ratio
parallel protocol tvl [--chain <chain>] # Total Value Locked per chain
parallel protocol supply <usdp|susdp|prl|sprl1|sprl2> [--chain <chain>] # Token supply breakdown
parallel protocol backing [--chain <chain>] # Collateral composition + backing ratio
parallel protocol chains # Supported chains + feature matrix
parallel protocol collaterals --chain <chain> # Accepted collaterals · caps · fees
parallel protocol mint-capacity --collateral <symbol> --chain <chain> [--amount <n>]
parallel protocol fees [--collateral <symbol>] [--chain <chain>]
parallel protocol savings-rate [--chain <chain>] # Global APY + per-chain breakdown
parallel protocol exchange-rate [--chain <chain>] # sUSDp / USDp rate
parallel protocol solvency [--chain <chain>] # Backing ratio per chain
parallel protocol surplus [--chain <chain>] # Pending surplus (USDp)
parallel protocol flashloan [--chain <chain>] # Flash loan capacity + fee rate
# Historical data (daily series) — flags: [--from <YYYY-MM-DD>] [--to <YYYY-MM-DD>] [--chain <chain>]
parallel protocol backing --history # reserves over time, per chain (source: Parallel API)
parallel protocol tvl --history # TVL over time (source: DeFiLlama)
parallel protocol savings-rate --history # sUSDp APY over time (source: DeFiLlama)
parallel protocol supply --history usdp # USDp circulating supply over time (source: DeFiLlama)parallel swap
Build Parallelizer transactions (mint / redeem / redeem-all).
# Get a quote first
parallel swap quote --op mint --collateral <symbol> --amount <n> --chain <chain>
parallel swap quote --op redeem --collateral <symbol> --amount <n> --chain <chain>
parallel swap quote --op redeem-all --amount <n> --chain <chain>
# Dry-run (shows raw unsigned tx — no wallet needed)
parallel swap mint --collateral <symbol> --amount <n> --chain <chain> --dry-run
parallel swap redeem --collateral <symbol> --amount <n> --chain <chain> --dry-run
parallel swap redeem-all --amount <n> --chain <chain> --dry-run
# Sign and broadcast
parallel swap mint --collateral <symbol> --amount <n> --chain <chain> --wallet
parallel swap redeem --collateral <symbol> --amount <n> --chain <chain> --wallet
parallel swap redeem-all --amount <n> --chain <chain> --wallet # proportional redeemCommand names match the MCP tools:
redeemtargets ONE collateral (formerlyburn),redeem-allredeems proportionally across the whole basket (formerlyredeem). The oldswap burnkeeps working as a hidden legacy command.
Options:
| Flag | Default | Description |
|---|---|---|
| --op <mint\|redeem\|redeem-all> | | Operation (quote only) |
| --collateral <symbol> | | Collateral symbol (e.g. USDC, USDS, sUSDS) |
| --amount <n> | | Amount in USDp (or collateral units for mint) |
| --slippage <pct> | 0.5 | Slippage tolerance in % (mint / redeem / redeem-all) |
| --address <0x...> | | Receiver address for dry-run (no key needed) |
parallel savings
sUSDp savings operations.
parallel savings rate [--chain <chain>] # APY + exchange rate + TVL
parallel savings estimate --amount <n> --duration <days> # Projected yield
parallel savings balance <address> [--chain <chain>] # sUSDp balance + USDp equivalent
# Dry-run
parallel savings deposit --amount <n> --chain <chain> --dry-run
parallel savings withdraw --amount <n> --chain <chain> --dry-run # amount in sUSDp shares
# Sign and broadcast
parallel savings deposit --amount <n> --chain <chain> --wallet
parallel savings withdraw --amount <n> --chain <chain> --walletNote: in a terminal,
savings depositshows an interactive menu with two methods: EIP-3009depositWithAuthorization(no approval transaction — two EIP-712 signatures, one on-chain transaction) or standard approve + deposit (two transactions). In JSON / non-TTY mode the standard path is used automatically.
parallel bridge
LayerZero cross-chain bridge (USDp).
parallel bridge quote --from <chain> --to <chain> --amount <n>
parallel bridge fees --from <chain> # fees to all destinations
parallel bridge limits [--chain <chain>] # daily + global limits
# Dry-run
parallel bridge send --from <chain> --to <chain> --amount <n> --dry-run
# Sign and broadcast
parallel bridge send --from <chain> --to <chain> --amount <n> --wallet [--recipient <address>]
parallel bridge status <txHash> --from <chain>
parallel bridge history <address> [--limit <n>] [--offset <n>] # past transfers (default: 20)Note: only
usdpcan be bridged for now (--tokendefaults to it; other values are rejected). Every supported chain can bridge.
parallel staking
PRL staking — sPRL1 (single-sided) and sPRL2 (BPT, Balancer).
parallel staking overview # global stats: supply, voting power, rewards, sPRL1 and sPRL2 per chain
parallel staking info <address> # per-address: staked per chain, voting power, pending and legacy rewards, sPRL2 v1 to migrate
parallel staking cooldown <address> # cooldown status + withdrawal requests of every chain
# Reads cover every chain with a vault: sPRL1 on Ethereum, Polygon, Base and Sonic,
# sPRL2 v2 on Ethereum and Base. stake runs on Ethereum or Base (--chain base), unstake on
# the chain of the position. Polygon and Sonic are being sunset (unstake and withdraw only).
# Without --chain, stake and unstake use Ethereum, even with a default chain configured.
# sPRL1 — stake PRL on Ethereum
parallel staking stake --type sprl1 --amount <n> --dry-run
parallel staking unstake --type sprl1 --amount <n> --dry-run
parallel staking stake --type sprl1 --amount <n> --wallet
parallel staking unstake --type sprl1 --amount <n> --wallet # initiate cooldown
parallel staking unstake --type sprl1 --wallet # execute ready withdrawals
# sPRL2 — stake on Ethereum (Balancer V3 BPT held by the sPRL2 v2 vault, multi-step)
parallel staking stake --type sprl2 --amount <n> --dry-run # BPT path (BPT of the chosen chain's pool)
parallel staking stake --type sprl2 --amount <n> --weth-amount <n> --dry-run # PRL+WETH path (--amount in PRL)
parallel staking unstake --type sprl2 --amount <n> --wallet # initiate cooldown
parallel staking unstake --type sprl2 --wallet # execute ready withdrawals
# Claim rewards: one Merkle claim on Base covers sPRL1 and sPRL2 (--type only filters the rewards listed),
# plus one on Polygon for the epochs published before the migration to Base, while any remain
parallel staking claim --type sprl1 --dry-run
parallel staking claim --type sprl2 --dry-run
parallel staking claim --type all --walletparallel gov
Snapshot governance — read-only.
parallel gov proposals [--status active|pending|passed|rejected] [--type PIP|PGP|PIR]
parallel gov proposal <PIP-65|0xabc...> # full details + vote results + description
parallel gov params # protocol parameters: fee rates, collateral capsparallel pay
x402 payment layer.
# Explore what's available on a chain
parallel pay capabilities [--chain <chain>]
# Gas sponsoring quota for an address
parallel pay quota <address> [--chain <chain>]
# Verify a payment on-chain
parallel pay verify <txHash> --chain <chain>
# Build (and sign) one payment, or run a long-lived signer (see below)
parallel pay prepare …
parallel pay engineNote:
confirmed: truemeans the facilitator actually settled this hash as an x402 payment — not merely that the transaction is mined. The response also carriesx402PaymentandtxMinedseparately: a mined transaction that is not an x402 payment returns{ confirmed: false, x402Payment: false, txMined: true }; an unknown hash returns all threefalse. Route, amount, token and recipient (route,amount,tokenOut,paidTo) are filled only for confirmed payments.
parallel pay prepare
Builds a signed or unsigned x402 payment payload.
# Unsigned — shows route + payload (no wallet required)
parallel pay prepare \
--amount <n> \
--recipient <address> \
--chain <chain>
# Signed — reads wallet balance, auto-selects best route, encodes the payload
parallel pay prepare \
--amount <n> \
--recipient <address> \
--chain <chain> \
--wallet
# Force which token the agent pays with
parallel pay prepare --amount <n> --recipient <address> --chain <chain> --pay-with <usdp|usdc|susdp>
# Metered payment (scheme upto): sign --amount as a cap, the merchant charges its actual cost
parallel pay prepare --amount <cap> --recipient <address> --chain <chain> --scheme upto --valid-for 600 --wallet--token controls what the merchant receives (default: usdp). Accepted values: usdp · usdc · susdp · any backing collateral symbol (susds, sfrxusd, …) · a token contract address.
--pay-with overrides which token the agent pays with (default: auto-detected from wallet balance). Accepted values: usdp · usdc · susdp.
--scheme selects the payment scheme (default: exact). With upto, --amount is a cap: the authorization is signed for the cap towards the UptoEscrow contract, the merchant settles the amount it actually charges and the escrow refunds the rest to the agent in the same transaction (Route U). Metered payments are USDp only — --token and --pay-with other than usdp are refused. --valid-for <seconds> (upto only, 1–3600, default 300) sets how long the authorization stays valid; pass the maxTimeoutSeconds the merchant announced in its 402. Available on chains where the escrow is deployed (see parallel pay capabilities).
Auto-route selection (when --wallet is provided):
| --token (merchant receives) | Agent wallet | Route | Method |
|---|---|---|---|
| usdp | USDp ≥ amount | A | transferWithAuthorization |
| usdp | sUSDp only | H | redeemWithAuthorization → USDp |
| usdp | sUSDp + USDp (top-up) | L | partialRedeemWithAuthorization |
| usdc | USDp | B | swapExactOutputWithAuthorization |
| usdc | sUSDp | I | redeemWithAuthorization → USDC |
| usdc | USDC | F | transferWithAuthorization |
| susdp | USDp | C | depositWithAuthorization |
| susdp | sUSDp | J | transferWithAuthorization |
| collateral (e.g. susds) | USDp | D | swapExactOutputWithAuthorization |
| collateral (e.g. susds) | sUSDp | K | redeemWithAuthorization → collateral |
| usdp with --scheme upto | USDp ≥ cap | U | uptoWithAuthorization (cap signed, actual charged, rest refunded) |
Without --wallet, the payload is unsigned and tokenIn defaults to usdp.
parallel pay engine
A long-lived signer for a program that pays often, such as an agent's HTTP client: the wallet loads once, then each payment costs one round trip on stdin and stdout instead of a process launch.
PARALLEL_PRIVATE_KEY=0x… parallel pay engineThe engine reads PARALLEL_PRIVATE_KEY at startup and announces itself with one JSON line on stdout:
{"ready":true,"version":"…","payer":"0x…","protocol":1}Without a key it writes one error envelope (WALLET_REQUIRED) on stderr and exits with code 1. A malformed key (not 0x followed by 64 hex characters) fails the CLI's startup check, before the engine starts, like every other command: exit code 1, no envelope.
It then reads one request per line on stdin and writes one answer per line on stdout. A request carries the options of pay prepare and an id of your choice (1 to 64 characters) that the answer carries back:
{"id":"1","amount":"0.5","recipient":"0x…","chain":"base"}
{"id":"2","amount":"0.01","recipient":"0x…","chain":"base","scheme":"upto","validitySeconds":600}| Field | Required | Same rule as |
|---|---|---|
| id | yes | |
| amount | yes | --amount |
| recipient | yes | --recipient |
| chain | yes | --chain (the global --chain flag and the configured default chain are ignored) |
| token | no, default usdp | --token |
| payWith | no | --pay-with |
| scheme | no, default exact | --scheme |
| validitySeconds | no, upto only, a number | --valid-for |
Error messages name the pay prepare option of the table, not the request field.
The answer is the data of pay prepare --wallet env --json, signed and encoded, or the error envelope pay prepare would give, under the request's id:
{"id":"1","success":true,"data":{"route":"A","method":"transferWithAuthorization","gasEstimate":65000,"signedPayload":{…},"encoded":"…"}}
{"id":"2","success":false,"error":{"code":"INSUFFICIENT_BALANCE","message":"…","hint":"…"}}Requests run concurrently and are answered as each completes, so the answers can come back in another order than the requests: match them by id. A failed request is answered and never stops the engine. An empty or blank line is ignored. A line that is not a JSON object with a valid id, or longer than 65,536 characters (as JavaScript counts them, in UTF-16 units), is answered with "id": null and the code INVALID_REQUEST. Nothing else is ever written on stdout. The engine lives as long as stdin: it exits with code 0 when stdin ends, once the requests still running are answered. A request still running 60 s after that is abandoned: it gets no answer, and the process exits once its network calls have ended. Under Node, a write to a closed stdout also ends the engine quietly.
The engine signs and never talks to the facilitator. Balances are read as pay prepare reads them, so no request accounts for what the other requests in flight have signed: sign only what the wallet can settle.
Utilities
parallel version # CLI version · MCP server version · Node version
parallel health [--chain <chain>] # RPC latency check for all chains (or one)
parallel config init # interactive setup wizard
parallel config get <key> # e.g. network.default_chain
parallel config set <key> <value> # e.g. parallel config set network.default_chain ethereumOutput
All commands print a human-readable table by default.
# Force JSON
parallel protocol tvl --json
# {"success":true,"data":{...}}
# Pipe-friendly: JSON is automatic when stdout is not a TTY
parallel protocol tvl | jq '.data.totalTVL'JSON envelope:
{ "success": true, "data": { ... } }
{ "success": false, "error": { "code": "CHAIN_SUNSET", "message": "...", "hint": "..." } }Streams and exit codes: success envelopes are written to stdout; error envelopes are written
to stderr, and the process exits with code 1 (0 on success). stdout carries data only — on a
failure it stays empty, so parallel … --json | jq prints nothing when the command errors. Read
errors from stderr or branch on the exit code (hint is optional):
parallel protocol tvl --chain nope --json 2>err.json || jq -r '.error.code' err.json
# → UNKNOWN_CHAINSupported chains
ethereum · base · arbitrum · optimism · polygon · avalanche · bsc · scroll · gnosis · sei · berachain · hyperevm · unichain · ink · tac · linea · xlayer · fraxtal · worldchain · hemi · plume · plasma · katana
sonicis sunset (PIP-64) — Parallelizer/Savings operations returnCHAIN_SUNSET; USDp bridging and balance reads stay active.
Not all chains support every feature. Use parallel protocol chains to see the full feature matrix (Parallelizer, sUSDp, bridge).
Error codes
| Code | Trigger |
|---|---|
| CHAIN_SUNSET | Operation not available on a sunset chain |
| PARALLELIZER_NOT_DEPLOYED | Feature requires Parallelizer, not deployed on this chain |
| UNKNOWN_CHAIN | Chain identifier not recognised |
| INSUFFICIENT_BALANCE | Wallet balance too low for the requested amount |
| COLLATERAL_NOT_SUPPORTED | Collateral (or bridge token) not accepted on this chain |
| WALLET_REQUIRED | Signing requested but no wallet available (--wallet unresolvable) |
| INVALID_KEYSTORE | Keystore unreadable, invalid format, or wrong decryption password |
| INVALID_AMOUNT | Negative, zero, malformed, or sub-decimal amount / duration / slippage |
| MISSING_OPTION | Required flag, argument, or subcommand not provided |
| INVALID_OPTION | A provided flag or argument has an invalid value |
| UNKNOWN_COMMAND / UNKNOWN_OPTION | Typo in a command or flag name |
| INVALID_ADDRESS | Argument is not a valid EVM address |
| CONFIG_KEY_NOT_FOUND | config get on a key absent from the config file |
| RPC_ERROR | An HTTP provider (facilitator, history APIs) is unreachable |
| INVALID_REQUEST | pay engine: a line on stdin is not a request (not JSON, not an object, no valid id, or longer than 65,536 characters) |
| TX_REVERTED | A transaction of a multi-step broadcast (e.g. the approval before a swap, deposit or stake) reverted, so the remaining ones were not sent |
| UNKNOWN | Any other failure |
Protocol-layer errors (caps, bridge limits, sunset chains, partial reads…) are surfaced with their own code in the code field — e.g. EXCEEDS_CAP, EXCEEDS_BRIDGE_LIMIT, EXCEEDS_GLOBAL_LIMIT, CHAIN_SUNSET, ROUTE_UNAVAILABLE, PARTIAL_READ, PARALLELIZER_NOT_DEPLOYED, COLLATERAL_NOT_SUPPORTED, INSUFFICIENT_BALANCE. Match on error.code; the message carries the human detail (remaining capacity, valid values…) without the code prefix.
Related
- parallel-mcp — MCP server + x402 facilitator
- Parallel Protocol
