| name | vara-wallet |
| description | Use when an agent needs to interact with Vara Network on-chain — deploy programs, call Sails methods, manage wallets, transfer tokens, monitor events. Not for building Sails programs (use vara-skills for that). |
Vara Wallet
Role
Use this skill for on-chain interaction with Vara Network: deploying programs, calling Sails methods, managing wallets, transferring VARA, querying state, and monitoring events.
Do NOT use this skill for:
- Writing Sails Rust programs — use
vara-skills/skills/sails-feature-workflow/
- Running gtest — use
vara-skills/skills/sails-gtest/
- Setting up Rust/Gear toolchain — use
vara-skills/skills/sails-dev-env/
Setup
if command -v vara-wallet &>/dev/null; then
VW="vara-wallet"
else
npm install -g vara-wallet
VW="vara-wallet"
fi
Zero-Setup Wallet
On first use, create a wallet. Encryption and passphrase are automatic — no human setup required.
$VW wallet create --name agent
$VW wallet list
The passphrase is stored at ~/.vara-wallet/.passphrase (0600). The agent never sees or handles it.
Command Quick Reference
Read (no account needed)
| Command | Purpose |
|---|
$VW node info | Chain name, genesis, latest block |
$VW balance [address] | Account balance in VARA |
$VW program info <id> | Program status and codeId |
$VW program list [--count N] [--all] | List on-chain programs (default: 100) |
$VW code info <codeId> | Code blob metadata |
$VW code list [--count N] | List uploaded code blobs |
$VW call <pid> Service/Query --args '[]' [--idl <path>] | Sails read-only query (free; IDL auto-resolved from on-chain WASM when embedded) |
$VW discover <pid> [--idl <path>] | Introspect Sails services, methods, events (IDL auto-resolved) |
$VW idl import <path.idl> (--code-id <hex> | --program <hex|ss58>) | Seed local IDL cache for contracts without an embedded IDL |
$VW idl list | List cached IDL entries |
$VW idl clear [--yes] | Wipe IDL cache (bare = preview, --yes = commit) |
$VW metadata list | List cached runtime-metadata entries (saves ~750ms per warm connect) |
$VW metadata clear [--yes] | Wipe runtime-metadata cache (bare = preview, --yes = commit) |
$VW state read <pid> | Read raw program state |
$VW mailbox read [address] | Read mailbox messages |
$VW inbox list [--since <duration>] [--limit <n>] | Query captured mailbox messages from event store |
$VW inbox read <messageId> | Read a specific captured message |
$VW events list [--type <t>] [--since <d>] [--program <id>] | Query captured events from event store |
$VW events prune [--older-than <duration>] | Delete old events |
$VW query <pallet> <method> [args...] | Generic storage query |
$VW vft balance <token> [account] --idl <path> | Fungible token balance |
$VW dex pairs --factory <addr> | List DEX trading pairs |
$VW dex pool <t0> <t1> --factory <addr> | Pool reserves and prices |
$VW dex quote <tIn> <tOut> <amount> --factory <addr> | Swap quote with price impact |
Write (account required — add --account <name>)
| Command | Purpose |
|---|
$VW transfer <to> <amount> | Transfer VARA tokens (keep-alive, safe default) |
$VW transfer <to> --all | Drain the entire account (transferAll, closes the account) |
$VW program upload <wasm> [--idl <path>] [--init <name>] [--args <json>] [--payload <hex>] [--value <v>] | Upload + init program (use --idl for auto-encoding) |
$VW program deploy <codeId> [--idl <path>] [--init <name>] [--args <json>] [--payload <hex>] [--value <v>] | Deploy from existing code (use --idl for auto-encoding) |
$VW code upload <wasm> | Upload code blob only |
$VW message send <dest> [--payload <hex>] [--value <v>] | Send message to any actor (program, user, wallet) — also usable for VARA transfers with custom payload |
$VW message reply <mid> [--payload <hex>] | Reply to a message |
$VW mailbox claim <messageId> | Claim value from mailbox message |
$VW call <pid> Service/Function --args '[...]' --value <v> --units human|raw --idl <path> | Sails state-changing call |
$VW call <pid> Service/Function --estimate --idl <path> | Estimate gas cost without sending |
$VW vft transfer <token> <to> <amount> --idl <path> | Transfer fungible tokens |
$VW vft approve <token> <spender> <amount> --idl <path> | Approve token spender |
$VW dex swap <tIn> <tOut> <amount> --factory <addr> [--slippage <bps>] | Swap tokens (auto-approves) |
$VW dex add-liquidity <t0> <t1> <a0> <a1> --factory <addr> | Add pool liquidity |
$VW dex remove-liquidity <t0> <t1> <lp> --factory <addr> | Remove pool liquidity |
$VW voucher issue <spender> <value> | Issue gas voucher |
$VW voucher revoke <spender> <voucherId> | Revoke voucher |
Any write command with --voucher <id> | Use voucher for sponsored execution |
$VW tx <pallet> <method> [args...] | Submit generic extrinsic |
Monitor
| Command | Purpose |
|---|
$VW wait <messageId> [--timeout <s>] | Wait for message reply |
$VW watch <pid> | Stream program events (NDJSON) |
$VW subscribe blocks [--finalized] | Stream new/finalized blocks (NDJSON + SQLite) |
$VW subscribe messages <pid> [--type <event>] | Stream program messages/events |
$VW subscribe mailbox <address> | Capture mailbox messages (survives between runs) |
$VW subscribe balance <address> | Stream balance changes |
$VW subscribe transfers [--from <a>] [--to <a>] | Stream transfer events |
$VW subscribe program <pid> | Stream program state changes |
Wallet Management
| Command | Purpose |
|---|
$VW wallet create [--name <n>] | Create encrypted wallet |
$VW wallet import [--seed <s>] [--mnemonic <m>] [--json <path>] | Import existing key |
$VW wallet list | List all wallets |
$VW wallet export <name> [--decrypt] [--output <path>] | Export keyring JSON (optionally to file) |
$VW wallet keys <name> | Export raw keys (address, publicKey, secretKeyPkcs8) |
$VW wallet default [name] | Get/set default wallet |
$VW init [--name <n>] | Initialize config + default wallet |
$VW config list | Show all config values |
$VW config set network testnet | Persist network endpoint |
$VW config set <key> <value> | Set any config key |
Common Workflows
Deploy and interact with a Sails program
UPLOAD=$($VW --account agent program upload ./target/wasm32-unknown-unknown/release/my_program.opt.wasm)
PROGRAM_ID=$(echo $UPLOAD | jq -r .programId)
$VW discover $PROGRAM_ID --idl ./target/idl/my_program.idl
$VW --account agent call $PROGRAM_ID MyService/DoSomething --args '["hello"]' --idl ./my_program.idl
$VW call $PROGRAM_ID MyService/GetState --args '[]' --idl ./my_program.idl
Send message and wait for reply
RESULT=$($VW --account agent message send $PROGRAM_ID --payload 0x00)
MSG_ID=$(echo $RESULT | jq -r .messageId)
REPLY=$($VW wait $MSG_ID --timeout 60)
echo $REPLY | jq .payload
Monitor program events
$VW watch $PROGRAM_ID | while read -r line; do
echo "$line" | jq .
done
Subscribe to events (with persistence)
$VW subscribe mailbox $MY_ADDRESS
$VW subscribe transfers --count 1 --timeout 30
$VW inbox list --since 1h
$VW events list --type mailbox --limit 10
Token operations
$VW vft balance $TOKEN_PROGRAM --idl ./vft.idl
$VW --account agent vft transfer $TOKEN_PROGRAM $RECIPIENT 1000 --idl ./vft.idl
$VW --account agent vft approve $TOKEN_PROGRAM $SPENDER 1000 --idl ./vft.idl
Fund an account with a voucher
$VW --account sponsor voucher issue $SPENDER_ADDRESS 100 --duration 14400
Use a voucher for sponsored execution
$VW call $PROGRAM Counter/Increment --voucher $VOUCHER_ID
$VW message send $PROGRAM --payload 0x... --voucher $VOUCHER_ID
$VW vft transfer $TOKEN $TO 1000 --voucher $VOUCHER_ID
IDL Resolution
Sails commands (call, discover, vft, dex) need an IDL. Resolution order:
--idl <path> — local file, always works, takes precedence
- Local cache —
~/.vara-wallet/idl-cache/<codeId>.cache.json, populated by previous fetches or idl import
- Embedded
sails:idl section — auto-extracted from the program's on-chain WASM and cached. Since 0.18.0 the extractor reads the enveloped section format (1-byte version, 1-byte flags with deflate bit, payload) emitted by current sails-rs, with a raw UTF-8 fallback for older beta.1-era programs.
- Bundled IDLs — standard VFT and Rivr DEX IDLs ship in the binary for
vft / dex commands.
Embedded IDL is the default. Any contract built with current sails-rs ships its IDL inside the WASM, so call / discover / vft / dex work without --idl; the first invocation populates the cache and subsequent calls hit it for free. idl import is only for contracts that don't have an embedded sails:idl section:
$VW idl import ./my-program.idl --program <programId>
$VW idl import ./my-program.idl --code-id 0x<hex>
Output Parsing
All commands output JSON to stdout. Errors go to stderr as { error, code }.
$VW balance | jq -r .balance
RESULT=$($VW --account agent transfer $TO 1)
echo $RESULT | jq '.events[] | select(.section == "balances")'
$VW --verbose balance 2>/dev/null | jq .
Network Switching
$VW --network testnet balance
$VW --ws wss://testnet.vara.network balance
export VARA_WS=wss://testnet.vara.network
$VW config set network testnet
Endpoint resolution order: --ws > --network > VARA_WS env > config.wsEndpoint > default.
Connection timeout is 10s. Bad endpoints fail fast with TRANSPORT_ERROR instead of hanging — see Transport Errors below.
| Network | Endpoint | --network shorthand |
|---|
| Mainnet | wss://rpc.vara.network (default) | --network mainnet |
| Testnet | wss://testnet.vara.network | --network testnet |
| Local | ws://localhost:9944 | --network local |
Vara.eth mailbox claims
Use --chain vara-eth for the Ethereum-anchored rail. Its network presets are
mainnet, hoodi, and local; a claim requires an Ethereum V3 wallet selected
with --account and funded ETH for L1 gas.
$VW --chain vara-eth --network hoodi --account agent-eth \
vara-eth:mailbox claim 0xMIRROR 0xCLAIMED_ID --wait submitted
$VW --chain vara-eth --network hoodi --account agent-eth \
vara-eth:mailbox claim 0xMIRROR 0xCLAIMED_ID
$VW --chain vara-eth --network hoodi \
vara-eth:mailbox claim --resume 0xTX_HASH
$VW --chain vara-eth --network hoodi --account agent-eth \
vara-eth:mailbox claim --replace 0xTX_HASH
Claims use only the Ethereum RPC for submission, receipt polling, resume, and
replacement, so they do not open a Vara.eth validator connection. The wallet
stores recoverable, secret-free metadata in
~/.vara-wallet/vara-eth-transactions.db. It is best-effort: a local database
failure never invalidates an accepted broadcast, but the transaction cannot be
recovered after the process exits.
--replace is restricted to a saved pending claim and preserves the original
Mirror, claimedId, calldata, nonce, sender, and chain ID. Fresh EIP-1559
claims have 20% max-fee headroom; replacements use a fresh quote and at least a
12.5% bump. A saved legacy gas-price transaction remains in legacy fee mode.
Supplying both --max-fee-per-gas and --max-priority-fee-per-gas (wei) skips
fee quoting; a partial override still needs a quote. A confirmed claim means
the Mirror accepted ValueClaimingRequested; wait for ValueClaimed to know
the co-processor has completed the claim.
Transport Errors
Replaces the legacy CONNECTION_TIMEOUT (WS) and TIMEOUT / CONNECTION_FAILED (program/dex/vft/message RPC paths) codes — scripts grepping the old codes won't fire. Faucet HTTP CONNECTION_FAILED is unchanged. --light failures route through the same taxonomy.
Transport-layer failures (DNS, WS handshake, RPC disconnect, TLS, timeout) surface as structured TRANSPORT_ERROR with a reason subcode. The error carries endpoint, host (DNS path), and cause at top level (no meta envelope — formatError flattens CliError.meta into the emitted object):
{"code":"TRANSPORT_ERROR","reason":"dns_failure","error":"Cannot resolve host nonexistent-host","endpoint":"wss://nonexistent-host","host":"nonexistent-host","cause":"getaddrinfo ENOTFOUND nonexistent-host"}
reason taxonomy: dns_failure | connection_refused | timeout | ws_close_abnormal | protocol_mismatch | unreachable | tls_failure | unknown. Switch on it to decide retry vs. fail:
case "$(echo "$ERR" | jq -r '.reason // ""')" in
timeout|ws_close_abnormal) retry_with_backoff ;;
dns_failure|tls_failure|protocol_mismatch|connection_refused|unreachable) fail_fast ;;
esac
The retry bucket matches the wallet's own auto-retry (see AGENTS.md): timeout and ws_close_abnormal are transient WS / handshake blips that self-clear; the others are deterministic for the current endpoint and want an endpoint swap, not another attempt.
--verbose writes a [verbose] cause: code=<x>, message=<y> line to stderr immediately before the structured JSON (EPIPE-safe).
Units
1 VARA = 10^12 minimal units (12 decimals). Amounts default to VARA.
$VW transfer $TO 1.5
$VW transfer $TO 15000000000 --units raw
Existential deposit is ~10 VARA on mainnet.
Error Recovery
| Code | Meaning | Action |
|---|
NO_ACCOUNT | No signing account | Add --account <name> |
PASSPHRASE_REQUIRED | Encrypted wallet, no passphrase | Check ~/.vara-wallet/.passphrase exists |
DECRYPT_FAILED | Wrong passphrase | Verify passphrase file content |
TRANSPORT_ERROR | Transport-layer failure | Switch on .reason to decide retry vs. fail. See Transport Errors |
TX_TIMEOUT | Transaction didn't land in 60s | Retry — network congestion |
TX_FAILED | On-chain failure | Inspect .events in output |
IDL_NOT_FOUND | No embedded sails:idl and no cache/bundled match | Run idl import (the error pre-fills the command) or pass --idl <path>. See IDL Resolution |
METHOD_NOT_FOUND | Method not in IDL | Check discover output; cross-service hint is in the error |
INVALID_ARGS_FORMAT | --args not in expected shape | Use a JSON array: ["arg1","arg2"]. 1-arg struct methods also accept '{"field":...}' |
INVALID_ADDRESS | Wrong shape for actor_id | Use hex (0x + 64 chars), SS58, or 32-byte array. Field name in the message |
PROGRAM_ERROR | Program execution failed | Read top-level reason (panic/unreachable/inactive/not_found) and programMessage (bare Sails error variant, Result::unwrap wrapper stripped). State problems (e.g. BetTokenTransferFromFailed) are not gas problems |
Guardrails
- Never pass secrets (seeds, mnemonics, passphrases) as CLI arguments in committed scripts. Use wallet files.
- Never use
--show-secret in automated flows. Secrets should stay in encrypted wallet files.
- Always use
--account <name> for signing, not --seed.
- Gas is auto-calculated — omit
--gas-limit unless you have a specific reason.
- Messages are async. After
message send, use wait to get the reply.
call auto-detects queries vs functions — no need to specify.
- If
sails-local-smoke is green and you need to interact with a deployed program on a live network, switch to this skill.