Skip to main content
All vault operations — setup, deposits/withdrawals, and every supported strategy (Kamino, Spot, Trustful) — now live in a single repository: github.com/voltrxyz/sdk-scripts. It replaces the old per-protocol script repos (kamino-scripts, spot-scripts, trustful-scripts, lend-scripts, drift-scripts, client-raydium-clmm-scripts) with two complementary entry points:

CLI

The recommended interface for vault managers and operators. Every operation is one <group>:<action> command driven by a JSON profile and per-call flags. No source edits to change runtime values.

Programmatic examples

Runnable TypeScript for developers embedding the operation builders directly in their own services, bots, or transaction pipelines. One self-contained file per action.
The CLI and examples both wrap the lower-level @voltr/vault-sdk and the protocol adapter packages. Use the CLI for routine operations; drop down to the SDK or examples when you embed Voltr in your own code.
Driving these operations from a coding agent? The Voltr Agent Skill ships vault-manager-cli.md and vault-manager-sdk.md references plus runnable examples/sdk/ flows (create vault, add adaptor + init strategy, allocate, read state).

Repository layout

PathWhat it is
apps/cliThe unified CLI (@voltr/scripts-cli) exposing every <group>:<action> command
packages/coreShared vault behavior — signers, RPC, token accounts, lookup tables, transaction modes (@voltr/scripts-core)
packages/kamino · packages/spot · packages/trustfulPer-integration adapter packages (account derivation + instruction building)
examples/One runnable TypeScript file per action, for SDK consumers
docs/Canonical guides: operator-guide.md, kamino.md, spot.md, trustful.md, adaptor-admin.md, architecture.md

Install

git clone https://github.com/voltrxyz/sdk-scripts.git
cd sdk-scripts
pnpm install
Validate the bundled example profile. This is fully offline — no RPC, no keypair, no deployed vault — and the quickest way to confirm your checkout works:
pnpm cli -- --profile configs/examples/usdc.mainnet.example.json check
pnpm cli -- <args>: the leading -- separates pnpm’s own arguments from the CLI’s. Flags after it (--profile, --mode, …) are parsed normally.

Profiles

A profile is one JSON file describing one vault deployment: its cluster, asset, optional lookup table, and any integration addresses. Routine operational values live in the profile; per-call values are flags; secrets are never in the profile. The profile is validated with zod on load, so a missing or malformed field fails before any RPC call.
# Copy the example and edit it:
cp configs/examples/usdc.mainnet.example.json configs/my-vault.json
#   vault.assetMintAddress / vault.assetTokenProgram  (required)
#   vault.vaultAddress                                (leave empty until vault:init prints it)
#   integrations.<adapter>.*                          (only the adapters you use)
{
  "name": "my-vault",                       // required, non-empty — use neutral names
  "cluster": "mainnet-beta",                // localnet | devnet | mainnet-beta
  "rpcUrl": "",                             // optional fallback RPC (env/flag override it)
  "vault": {
    "name": "My USDC Vault",                // optional display label
    "assetMintAddress": "<ASSET_MINT>",     // required
    "assetTokenProgram": "<TOKEN_PROGRAM>", // required (Token or Token-2022)
    "vaultAddress": "<VAULT_ADDRESS>",      // generated by vault:init — leave empty until then
    "useLookupTable": false,
    "lookupTableAddress": "<LUT_ADDRESS>"   // required only when useLookupTable is true
  },
  "integrations": {
    "kamino":   { "reserveAddress": "<RESERVE>", "kvaultAddress": "<KVAULT>", "directWithdrawDiscriminator": [/* 8 bytes */] },
    "spot":     { "foreignMintAddress": "<MINT>", "foreignTokenProgram": "<PROG>", "assetOracleAddress": "<ORACLE>", "foreignOracleAddress": "<ORACLE>", "directWithdrawDiscriminator": [/* 8 bytes */] },
    "trustful": { "strategySeedString": "<SEED>" }
  }
}

Keypairs & roles

Commands sign as one of three roles. Each resolves its keypair path from a flag, falling back to an environment variable — the flag wins. If neither is set, the command fails up front naming both.
RoleFlagEnv varUsed by
admin--admin-keypairADMIN_KEYPAIRvault:init*, metadata, config, adaptor admin, harvest-fee
manager--manager-keypairMANAGER_KEYPAIRstrategy operations (Kamino/Spot/Trustful, claims)
user--user-keypairUSER_KEYPAIRdeposit/withdraw, direct-withdraw
# .env (gitignored) — copy .env.example and set once:
RPC_URL=https://your-rpc
ADMIN_KEYPAIR=/path/to/admin.json
MANAGER_KEYPAIR=/path/to/manager.json
USER_KEYPAIR=/path/to/user.json
No keypair material lives in profiles — only paths, via flag or env. Keep keypair files outside the repo, restrict permissions (chmod 600), and prefer env vars over flags so secret paths don’t land in shell history. The read-only *:query:* commands and check need no keypair.
The RPC URL resolves in this order (first non-empty wins): --rpc-url flag → RPC_URL env → HELIUS_RPC_URL env → rpcUrl in the profile.

Transaction modes — verify before you execute

Every transaction command takes --mode (default print). A command builds first (reading chain state over RPC, and the Jupiter API for swaps), then dispatches the mode — so a reachable RPC is required in every mode except check.
ModeSends on-chain?What it does
print (default)NoPrints { label, instructionCount, lookupTableAddresses } + operation metadata. The no-send preview.
simulateNoOne simulateTransaction RPC; prints OK/FAILED, compute units, program logs, explorer URL.
multisigNoEmits a base64/base58 transaction message to import into a multisig (e.g. Squads). Requires --multisig-address.
executeYesSigns with the role keypair, sends, confirms; prints the signature and compute units.
Never execute blind. Preview with --mode print, confirm with --mode simulate, then --mode execute. For a multisig-controlled vault, use --mode multisig --multisig-address <VAULT_SIGNER_PDA> and import the printed payload instead of signing locally.
# 1. print: confirm the plan builds and the instruction/LUT counts look right
pnpm cli -- --profile configs/my-vault.json --mode print    vault:deposit --amount 1000000
# 2. simulate: confirm it would succeed on-chain
pnpm cli -- --profile configs/my-vault.json --mode simulate vault:deposit --amount 1000000
# 3. execute: send it for real
pnpm cli -- --profile configs/my-vault.json --mode execute  vault:deposit --amount 1000000

Global options

OptionPurpose
--profile <path>Profile JSON to load (required)
--rpc-url <url>RPC override
--mode <mode>print (default) / simulate / multisig / execute
--multisig-address <pubkey>On-chain signer PDA; required for --mode multisig
--priority-fee <kind>helius (default) / rpc / fixed / none
--priority-fee-micro-lamports <n>microLamports for --priority-fee fixed
--compute-unit-limit <n>Override the estimated compute-unit limit

Command groups

Commands are grouped by <group>:* prefix. The CLI itself is the authoritative reference for the full command surface and every flag:
pnpm cli -- --help                 # global options + all command groups
pnpm cli -- vault:deposit --help   # flags for a single command
GroupCommandsReference
Vault (core)vault:init, vault:init-and-set-token-metadata, vault:set-token-metadata, vault:update-config, vault:accept-admin, vault:harvest-fee, vault:deposit, vault:request-withdraw, vault:withdraw, vault:cancel-request-withdraw, vault:instant-withdraw, vault:query:position, vault:query:strategy-positionsoperator-guide.md
Adaptor adminvault:add-adaptor, vault:remove-adaptor, vault:init-direct-withdrawadaptor-admin.md
Kaminokamino:market:{init,deposit,withdraw,claim-reward}, kamino:kvault:{init,deposit,withdraw,claim-reward-with-index,direct-withdraw,request-and-direct-withdraw}kamino.md
Spotspot:swap:{init,buy,sell}, spot:earn:{init,extend-lut,deposit,withdraw,init-direct-withdraw}, spot:query:strategy-positionsspot.md
Trustfultrustful:arbitrary:{init,deposit,withdraw}, trustful:curve:{init,borrow,repay,remove}trustful.md
Maintenancecheck (offline profile validation)

Recipes

The following assume RPC_URL and the role keypairs are set in .env. Run each with --mode print then --mode simulate before --mode execute.
# admin signs; manager is just an address. A fresh vault keypair is generated and its address printed.
pnpm cli -- --profile configs/my-vault.json --mode execute \
  vault:init --manager <MANAGER_PUBKEY> --name "My USDC Vault" --max-cap 100000000000

# Record the printed "Generated vault address" as vault.vaultAddress in the profile.

# (alt) init + LP-token metadata in one transaction:
pnpm cli -- --profile configs/my-vault.json --mode execute \
  vault:init-and-set-token-metadata --manager <MANAGER_PUBKEY> --name "My USDC Vault" \
  --max-cap 100000000000 --metadata-name "My USDC Vault Token" --metadata-symbol MYUSDC \
  --metadata-uri https://example.com/metadata.json
vault:init cannot use --mode multisig: a fresh vault keypair must sign initialization, which a multisig payload can’t carry. Use --mode execute.
vault:add-adaptor defaults to the Kamino adaptor; pass --adaptor-program for the others.
# Kamino (default adaptor):
pnpm cli -- --profile configs/my-vault.json --mode execute vault:add-adaptor
# Spot:
pnpm cli -- --profile configs/my-vault.json --mode execute \
  vault:add-adaptor --adaptor-program EW35URAx3LiM13fFK3QxAXfGemHso9HWPixrv7YDY4AM
# Trustful:
pnpm cli -- --profile configs/my-vault.json --mode execute \
  vault:add-adaptor --adaptor-program 3pnpK9nrs1R65eMV1wqCXkDkhSgN18xb1G5pgYPwoZjJ

# Register a direct-withdraw strategy (Kamino kvault default + profile discriminator):
pnpm cli -- --profile configs/my-vault.json --mode execute vault:init-direct-withdraw
# Spot Earn uses its own command (derives the strategy for you):
pnpm cli -- --profile configs/my-vault.json --mode execute spot:earn:init-direct-withdraw
pnpm cli -- --profile configs/my-vault.json --mode execute vault:deposit --amount 1000000

# Read-only position check (ignores --mode, no keypair):
pnpm cli -- --profile configs/my-vault.json vault:query:position --user <USER_PUBKEY>

# Standard withdraw: request, wait the lock period, then claim:
pnpm cli -- --profile configs/my-vault.json --mode execute vault:request-withdraw --amount 1000000   # or --all / --in-lp
pnpm cli -- --profile configs/my-vault.json --mode execute vault:withdraw

# Instant withdraw against idle assets (no waiting period):
pnpm cli -- --profile configs/my-vault.json --mode execute vault:instant-withdraw --amount 1000000   # or --all / --in-lp
pnpm cli -- --profile configs/my-vault.json --mode execute kamino:market:init
pnpm cli -- --profile configs/my-vault.json --mode execute kamino:market:deposit  --amount 1000000
pnpm cli -- --profile configs/my-vault.json --mode execute kamino:market:withdraw --amount 1000000

pnpm cli -- --profile configs/my-vault.json --mode execute kamino:kvault:init
pnpm cli -- --profile configs/my-vault.json --mode execute kamino:kvault:deposit  --amount 1000000
pnpm cli -- --profile configs/my-vault.json --mode execute kamino:kvault:withdraw --amount 1000000

# Claim one farm reward (omit --swap-amount when the reward mint == asset mint):
pnpm cli -- --profile configs/my-vault.json --mode execute kamino:market:claim-reward \
  --reward-mint <REWARD_MINT> --farm-state <FARM_STATE> --user-state <USER_STATE> \
  --swap-amount <RAW_REWARD_AMOUNT> --slippage-bps 50
Register the Spot adaptor first.
# Swap strategy: init, then buy (asset→foreign) / sell (foreign→asset)
pnpm cli -- --profile configs/my-vault.json --mode execute spot:swap:init
pnpm cli -- --profile configs/my-vault.json --mode execute spot:swap:buy  --amount 1000000 --slippage-bps 50
pnpm cli -- --profile configs/my-vault.json --mode execute spot:swap:sell --amount 1000000 --slippage-bps 50

# Jupiter Earn: init (then extend-lut if you use a LUT), deposit, withdraw
pnpm cli -- --profile configs/my-vault.json --mode execute spot:earn:init
pnpm cli -- --profile configs/my-vault.json --mode execute spot:earn:extend-lut
pnpm cli -- --profile configs/my-vault.json --mode execute spot:earn:deposit  --amount 1000000
pnpm cli -- --profile configs/my-vault.json --mode execute spot:earn:withdraw --amount 1000000
Register the Trustful adaptor first.
# Arbitrary strategy (deposit prints the withdrawal-holding account — return assets there before withdrawing):
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:arbitrary:init
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:arbitrary:deposit \
  --amount 1000000 --destination <DESTINATION_TOKEN_ACCOUNT> --position-value-after 1000000
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:arbitrary:withdraw \
  --amount 1000000 --position-value-after 0

# Curve strategy: init, borrow, repay, remove
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:curve:init
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:curve:borrow --amount 1000000 --borrow-rate-bps 50
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:curve:repay  --amount 1000000 --borrow-rate-bps 50
pnpm cli -- --profile configs/my-vault.json --mode execute trustful:curve:remove

Programmatic examples

For automation, bots, and services that embed Voltr directly, the examples/ directory holds runnable TypeScript — one self-contained file per action that imports the public operation builders, inspects a BuiltOperation, and routes it through the shared transaction processor. They are the lower-level programmatic path; the CLI remains the primary interface for routine operations.
# Configure via env (the examples read the same profile + RPC precedence as the CLI):
export VOLTR_PROFILE=configs/my-vault.json   # default if unset
export RPC_URL=https://your-rpc
export MANAGER_KEYPAIR=/path/to/manager.json # and/or ADMIN_KEYPAIR / USER_KEYPAIR

# List every example with its group, role, and network requirement:
pnpm examples:list

# Run a file directly (no arguments — config comes from env):
pnpm exec tsx examples/src/kamino/market-withdraw.ts

# …or by name through the dispatcher:
pnpm example -- kamino:market-withdraw
Per-run values (amounts, rates) are clearly marked constants at the top of each file. Every setting has both an env var and an equivalent flag (--profile, --rpc-url, --mode, --multisig-address, --yes); --help lists them.
Examples default to print and never send. execute is gated — type yes at the prompt, or set VOLTR_CONFIRM=1 for non-interactive runs. No secrets are committed: addresses come from your profile, keypairs from env vars.

Example catalog

GroupFiles
Vault / coreinitialize, deposit, request-withdraw, withdraw, instant-withdraw, cancel-request-withdraw, query-position, query-strategy-positions
Kaminomarket-{init,deposit,withdraw,claim-reward}, kvault-{init,deposit,withdraw,claim-reward,direct-withdraw,request-and-direct-withdraw}
Spotswap-{init,buy,sell}, earn-{init,deposit,withdraw,extend-lut,init-direct-withdraw}, query-strategy-positions
Trustfularbitrary-{init,deposit,withdraw}, curve-{init,borrow,repay,remove}
Compositionallocate — sequence builders from more than one package through the shared processor
Examples import only documented package exports (@voltr/scripts-core, @voltr/scripts-kamino, @voltr/scripts-spot, @voltr/scripts-trustful). See examples/README.md for the full catalog and the offline pnpm examples:check verification.

Where to go next

Supported Integrations

The adaptors and their CLI command groups

Strategy Setup Guide

Add adaptors and initialize strategies

Fund Allocation

Deploy idle funds into strategies

SDK Reference

The lower-level @voltr/vault-sdk builders