A Voltr vault manager bot that distributes funds equally across lending strategies on a fixed schedule. Built for Solana.
The bot runs several concurrent loops:
- Rebalance Loop — On every interval (default 30 min), computes an equal-weight target allocation across all strategies and executes deposit/withdraw transactions to reach it. Also triggers on new deposits detected via ATA subscription.
- Refresh Loop — Periodically refreshes strategy positions to keep on-chain receipt values up to date.
- Harvest Fee Loop — Collects protocol/admin/manager fees from the Voltr vault.
- Claim Reward Loops — Claims farm rewards from Kamino market and vault strategies, swaps them back to the vault asset via Jupiter.
Given N strategies and total funds T:
- Compute locked amounts per strategy (funds that can't be withdrawn due to liquidity constraints)
- Distribute remaining funds equally across all strategies
- Target idle balance = 0
Strategies with locked amounts exceeding the equal share keep their locked amount; the rest is redistributed among the remaining strategies.
| Type | Description |
|---|---|
jupiterLend |
Jupiter Lend |
kaminoMarket |
Kamino lending market reserve |
kaminoVault |
Kamino vault (multi-reserve) |
perena |
Perena USD* (our unified adaptor; address = Perena vault) |
p0 |
P0 / marginfi v2 lending bank (our unified adaptor; address = bank) |
The perena and p0 strategies go through our own Voltr adaptor (Hubra-labs/hubra-usdc-adaptor, checked out at /root/hubra-usdc-adaptor). One-time setup per vault and protocol: ENV_FILE=.env-usdc npx tsx scripts/setup-hubra-adaptor.ts (dry-run; --send to submit).
Configure strategies in strategies.json. Jupiter Lend strategy addresses are derived automatically from on-chain PDAs using the vault's ASSET_MINT_ADDRESS.
# Install dependencies
pnpm install
# Copy and fill in environment variables
cp .env.example .env
# Build
pnpm run build
# Run
pnpm start
# Dev mode (ts-node)
pnpm run dev- Fork this repo on Replit
- Open the Secrets tab and add each variable from
.env.example - For the manager keypair, set
MANAGER_SECRET_KEYto your base58 private key or JSON byte array (e.g.[174,47,154,...]) — do not useMANAGER_SECRET_PATHon Replit - Edit
strategies.jsonwith your strategies (Kamino strategies need anaddress; Jupiter Lend is derived automatically) - Hit Run — the
.replitfile handles build + start automatically
Replit free tier will sleep your repl after inactivity. For a 24/7 bot, use a paid Replit plan or deploy to a VPS.
| Variable | Description |
|---|---|
RPC_URL |
Solana RPC endpoint |
MANAGER_SECRET_PATH |
Path to the vault manager keypair JSON file (set this or MANAGER_SECRET_KEY) |
MANAGER_SECRET_KEY |
Manager private key as a base58 string or JSON byte array [n,n,n,...] (set this or MANAGER_SECRET_PATH) |
VOLTR_VAULT_ADDRESS |
Voltr vault address |
VOLTR_VAULT_ADMIN_ADDRESS |
Vault admin address |
VOLTR_VAULT_MANAGER_ADDRESS |
Vault manager address |
ASSET_MINT_ADDRESS |
Vault asset token mint |
ASSET_TOKEN_PROGRAM |
Token program for the asset (Token or Token-2022) |
VOLTR_LOOKUP_TABLE_ADDRESS |
Voltr address lookup table |
| Variable | Default | Description |
|---|---|---|
RPC_FALLBACK_URL |
— | Fallback RPC endpoint |
REBALANCE_LOOP_INTERVAL_MS |
1800000 |
Rebalance interval (30 min) |
REFRESH_LOOP_INTERVAL_MS |
600000 |
Refresh interval (10 min) |
HARVEST_FEE_LOOP_INTERVAL_MS |
1800000 |
Harvest fee interval (30 min) |
CLAIM_KMARKET_REWARD_LOOP_INTERVAL_MS |
3600000 |
Kamino market reward claim interval (1 hr) |
CLAIM_KVAULT_REWARD_LOOP_INTERVAL_MS |
3600000 |
Kamino vault reward claim interval (1 hr) |
DEPOSIT_STRATEGY_MIN_AMOUNT |
0 |
Min idle balance to trigger deposit-based rebalance |
JUPITER_SWAP_SLIPPAGE_BPS |
50 |
Slippage tolerance for Jupiter reward swaps |
REFRESH_MIN_POSITION_VALUE |
1000000 |
Min position value (lamports) to refresh |
REBALANCE_DEVIATION_BPS |
0 |
Reserved for future threshold-based rebalancing |
WORKER_MAX_MEMORY_MB |
2048 |
Max memory for rebalance worker thread (MB) |
HEALTH_SERVER_PORT |
8080 |
HTTP health check port |
LOG_LEVEL |
info |
Pino log level |
All feature flags default to true except where noted:
| Flag | Default | Description |
|---|---|---|
ENABLE_REBALANCE_LOOP |
true |
Equal-weight rebalancing |
ENABLE_REFRESH_LOOP |
true |
Position refresh |
ENABLE_HARVEST_FEE_LOOP |
true |
Fee harvesting |
ENABLE_CLAIM_KMARKET_REWARD_LOOP |
true |
Kamino market reward claims |
ENABLE_CLAIM_KVAULT_REWARD_LOOP |
false |
Kamino vault reward claims |
src/
index.ts # Entry point, spawns loops
config.ts # Environment config with Zod validation
rebalance_loop.ts # Equal-weight rebalance logic
rebalance_worker.ts # Worker thread wrapper for rebalance loop
refresh_loop.ts # Position refresh loop
harvest_fee_loop.ts # Fee harvesting loop
claim_kmarket_reward_loop.ts
claim_kvault_reward_loop.ts
lib/
connection.ts # RPC connection manager with failover
constants.ts # Program IDs and discriminators
convert.ts # Address conversion utilities
keypair.ts # Manager keypair loader (file or env var)
utils.ts # Logger, sleep, retry helpers
solana.ts # Transaction building and sending
price.ts # Token price fetching
jupiter.ts # Jupiter Lend strategy instructions + swap
strategy-config.ts # Strategy registry loaded from strategies.json (derives Jupiter addresses from PDAs)
kamino/
index.ts
instructions.ts # Kamino deposit/withdraw/claim instructions
reserves.ts # Kamino reserve APY and liquidity helpers
simulate/
index.ts # getCurrentAndEqualAllocation()
optimizer.ts # createEqualWeightAllocation()
types.ts # Allocation interface