solana-agent-wallet-ops is a CLI-first local toolkit for agent-driven Solana wallet operations. It is built for automation workflows, coding agents, and local scripts that need repeatable wallet-set storage, visibility into balances, and guarded bulk transfers.
This is not a consumer wallet app, not a GUI, and not tied to a single host such as OpenClaw, Codex, Claude Code, or Cursor. It is intended to be a reusable local primitive that those systems can wrap.
- Local automation and scripting workflows
- Agent/tool adapters that need wallet creation and transfer utilities
- Operators testing wallet batches on devnet before mainnet-beta use
- Teams that want local, scriptable wallet storage without adding external infrastructure
- Bulk create Solana wallets and save them as named wallet sets
- Import wallet JSON into local wallet-set storage
- List wallet sets or inspect wallets inside a set
- Store wallet sets in local SQLite with indexed lookups
- Check balances in bulk
- SOL by default
- SPL token balances when
--mint <TOKEN_MINT>is provided
- Export wallet addresses to CSV
- Bulk transfers with preview-first behavior
- SOL transfers by default
- SPL token transfers when
--mint <TOKEN_MINT>is provided
- SplitNOW integration for multi-wallet quote, order, and status workflows
- Devnet and mainnet-beta support
- Explicit
--executeguard for real sends - Dry-run friendly planning output
Requirements:
- Node.js 20+
pnpm
Install dependencies:
pnpm installOptional local build:
pnpm buildThis repo now has GitHub-backed release automation with Changesets.
Local release workflow:
- Run
pnpm changesetfor any user-visible change. - Commit the generated file in
.changeset/with the code change. - Merge to
main. - GitHub Actions opens or updates a
chore: version packagesPR. - After that PR is merged, GitHub Actions tags
v<version>and creates a GitHub Release.
Useful commands:
pnpm changeset
pnpm release:status
pnpm release:versionThis repo now exposes the repository root as the skill package for GitHub distribution:
SKILL.md
agents/openai.yml
That root-level skill is meant for Codex-style agents that need a safe, repeatable workflow for:
- creating wallet sets
- inspecting balances
- exporting public addresses
- previewing and executing SOL or SPL batch transfers
- running SplitNOW quote, order, and status flows
The skill points agents at the existing repo docs instead of copying them. It also bakes in the important operational rules:
- keep wallet DBs and API keys outside the repo
- use
devnetby default unless the user explicitly wantsmainnet-beta - preview before any live transfer
- preserve the sender rent reserve for SOL transfers
If an execution environment blocks pnpm tsx because of tsx IPC restrictions, agents can use the equivalent fallback form:
node --import tsx src/cli/<command>.ts ...Create a wallet set:
pnpm tsx src/cli/create-wallets.ts --set test-set --count 25 --network devnetCreate a wallet set with an explicit external DB path:
pnpm tsx src/cli/create-wallets.ts --set test-set --count 25 --network devnet --db-path ~/.solana-agent-wallet-ops/wallets.sqliteImport a wallet-set JSON file into SQLite storage:
pnpm tsx src/cli/import-wallets.ts --from ./wallet-set.jsonImport a standalone wallet JSON file as a one-wallet set:
pnpm tsx src/cli/import-wallets.ts --from ./wallet.json --set imported-treasury --network devnetList all wallet sets:
pnpm tsx src/cli/list-wallets.tsInspect one wallet set:
pnpm tsx src/cli/list-wallets.ts --set test-setCheck SOL balances:
pnpm tsx src/cli/balances.ts --set test-setCheck SPL balances for a mint:
pnpm tsx src/cli/balances.ts --set test-set --mint <TOKEN_MINT>Export addresses:
pnpm tsx src/cli/export-addresses.ts --set test-set --out ./exports/test-set.csvPreview a SOL distribution from the first wallet in one set to every wallet in another:
pnpm tsx src/cli/bulk-transfer.ts --from-set treasury --to-set campaign --amount 0.01 --dry-runExecute that SOL distribution:
pnpm tsx src/cli/bulk-transfer.ts --from-set treasury --to-set campaign --amount 0.01 --executePreview a CSV-based SOL distribution from a single source wallet file:
pnpm tsx src/cli/bulk-transfer.ts --from ./wallet.json --to-csv ./recipients.csv --amount 0.01 --network devnet --dry-runPreview an SPL token distribution:
pnpm tsx src/cli/bulk-transfer.ts --from-set treasury --to-csv ./token-recipients.csv --mint <TOKEN_MINT> --dry-runCreate a SplitNOW quote for splitting SOL across Solana wallets:
export SPLITNOW_API_KEY="YOUR_SPLITNOW_API_KEY"
pnpm tsx src/cli/splitnow-quote.ts --from-amount 10 --from-asset-id sol --from-network-id solanaPreview a SplitNOW order from an existing quote into a wallet set:
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-set campaignPreview a SplitNOW order from a CSV recipient list:
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-csv ./splitnow-recipients.csvPreview a SplitNOW order with a specific exchanger from the quote:
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-set campaign --exchanger changenowCreate the real SplitNOW order after review:
pnpm tsx src/cli/splitnow-order.ts --quote-id QUOTE123 --to-set campaign --executeTrack SplitNOW order status:
pnpm tsx src/cli/splitnow-status.ts --order-id ABC123splitnow-quotecreates a floating-rate quote and prints the exchangers returned by SplitNOW, sorted by estimated output.splitnow-orderconsumes an existing quote and builds a multi-wallet order from either--to-setor--to-csv.- The current implementation splits funds evenly across recipients. It does not support per-recipient custom percentages yet.
- The current implementation uses one exchanger per order. By default it selects
best, or you can pass--exchanger <id>to force one of the exchanger ids returned by the quote. - SplitNOW recipient sources are address-only:
- wallet set via
--to-set - CSV via
--to-csv
- wallet set via
- SplitNOW recipient CSV columns:
- required:
public_key - optional:
label
- required:
- SplitNOW orders reject empty recipient lists, duplicate recipient public keys, and recipient counts above 100.
- Before creating an order, the client checks SplitNOW deposit limits and rejects orders that are below the minimum required deposit for the selected asset and recipient count.
- Creating an order does not move funds by itself. It returns:
orderIddepositAddressdepositAmount
- After order creation, you still need to send the deposit to the returned address, then track progress with
splitnow-status. - Use
--api-urlonly when intentionally pointing at a non-default SplitNOW API endpoint.
Notes for CSV recipient input:
- If the CSV includes an
amountcolumn, per-row amounts are used. - If the CSV omits
amount, pass a global--amount. - CSV columns:
- required:
public_key
- required:
- optional:
label - optional:
amount
Storage path overrides:
- default DB path:
~/.solana-agent-wallet-ops/wallets.sqlite - CLI override:
--db-path <path> - env override:
SAWO_DB_PATH=/path/to/wallets.sqlite - repo-local DBs are rejected unless you pass
--allow-repo-dbor setSAWO_ALLOW_REPO_DB=1 - SplitNOW API key:
SPLITNOW_API_KEY=...
Wallet import formats:
- wallet-set JSON file with
set_name,created_at,network, andwallets - standalone wallet JSON object with
label,public_key, andsecret_key_base58 - JSON array of wallet entries when you also pass
--setand--network
Primary wallet storage lives in a local SQLite database:
~/.solana-agent-wallet-ops/wallets.sqlite
The DB contains:
wallet_setswallets
Each stored wallet entry includes:
labelpublic_keysecret_key_base58
For portability, bulk-transfer --from <file> still accepts:
- a standalone wallet JSON object
- a one-wallet wallet-set JSON object
Address exports remain safer and only write:
labelpublic_key
See docs/storage-format.md for examples.
- Secret-bearing storage defaults outside the repo at
~/.solana-agent-wallet-ops/wallets.sqlite. - Repo-local DB paths are blocked unless you opt in with
--allow-repo-dborSAWO_ALLOW_REPO_DB=1. - SQLite improves structure and performance. It does not encrypt private keys.
- SplitNOW API keys should stay in environment variables or untracked local files, not in the repo or shell history.
- Commands do not print secret keys unless
--show-secretsis explicitly passed. - CSV exports never contain secrets.
- Real transfers require
--execute. - Transfer commands print a plan before sending.
- Use devnet first.
- For SPL transfers, the sender still needs SOL to pay transaction fees and any associated token account creation rent for recipients that do not already have one.
- SplitNOW order creation only becomes funded after you send the requested deposit. Review the quote, exchanger, and recipient split first.
More detail: docs/safety.md
- CLI-first TypeScript with
commanderkeeps the toolkit scriptable and easy to wrap from different agents. - Local SQLite storage keeps state queryable, atomic, and scalable without adding an external service.
- Secret-bearing storage defaults outside the repo because
gitignoreis not a security boundary. - Wallet-set storage is asset-agnostic; SOL and SPL behavior is chosen at command time.
- SPL support is intentionally mint-driven instead of symbol-driven. The CLI fetches mint decimals on-chain and derives associated token accounts deterministically.
- SplitNOW integration is isolated in its own client and CLI commands so agent workflows can use quotes/orders without coupling the core wallet-storage path to a third-party exchange.
- Transfer execution is serial in v1 so failures are easier to reason about and partial completion is visible.
- Create a devnet wallet set:
pnpm tsx src/cli/create-wallets.ts --set test-set --count 3 --network devnet- Inspect the wallets:
pnpm tsx src/cli/list-wallets.ts --set test-set- Export addresses:
pnpm tsx src/cli/export-addresses.ts --set test-set --out ./exports/test-set.csv- Fund the first wallet in
test-setwith devnet SOL, then preview a distribution:
pnpm tsx src/cli/bulk-transfer.ts --from-set test-set --to-csv ./exports/test-set.csv --amount 0.001 --dry-run- Check balances:
pnpm tsx src/cli/balances.ts --set test-set- For SPL testing, mint or obtain a devnet token, then preview token balances and a distribution:
pnpm tsx src/cli/balances.ts --set test-set --mint <DEVNET_TOKEN_MINT>
pnpm tsx src/cli/bulk-transfer.ts --from-set test-set --to-csv ./token-recipients.csv --mint <DEVNET_TOKEN_MINT> --dry-run- For SplitNOW testing, create a quote, preview the order payload, then create the order:
export SPLITNOW_API_KEY="YOUR_SPLITNOW_API_KEY"
pnpm tsx src/cli/splitnow-quote.ts --from-amount 10 --from-asset-id sol --from-network-id solana
pnpm tsx src/cli/splitnow-order.ts --quote-id <QUOTE_ID> --to-set test-set
pnpm tsx src/cli/splitnow-order.ts --quote-id <QUOTE_ID> --to-set test-set --execute
pnpm tsx src/cli/splitnow-status.ts --order-id <ORDER_ID>- Optional at-rest encryption for local wallet storage
- Adapter packages for Codex, Claude Code, Cursor, and OpenClaw
- Configurable concurrency for read-only RPC calls
- Better structured output modes for automation (
json, exit codes by failure class) - SPL token account discovery beyond the associated token account path
- SplitNOW quote caching and exchanger-selection strategies beyond
best