Skip to main content
This guide explains how to use the Ranger Earn SDK to deposit and withdraw funds between the vault’s idle account and strategies.

Setup

import { Connection, Keypair, PublicKey, TransactionInstruction } from "@solana/web3.js";
import { VoltrClient, LENDING_ADAPTOR_PROGRAM_ID, SEEDS } from "@voltr/vault-sdk";
import { BN } from "@coral-xyz/anchor";
import {
  createAssociatedTokenAccountInstruction,
  getAssociatedTokenAddressSync,
  getAccount,
  TOKEN_PROGRAM_ID,
} from "@solana/spl-token";
import fs from "fs";
const managerKp = Keypair.fromSecretKey(
  Uint8Array.from(JSON.parse(fs.readFileSync("/path/to/manager.json", "utf-8")))
);
const manager = managerKp.publicKey;

const connection = new Connection("your-rpc-url");
const client = new VoltrClient(connection);

const vault = new PublicKey("your-vault-address");
const vaultAssetMint = new PublicKey("your-asset-mint");

Depositing Funds to Strategies

Accounts required for deposit are protocol-specific. Use the protocol-specific scripts:
Protocol / AdaptorDeposit Scripts
Kamino AdaptorKamino Vault, Kamino Lending Market
Drift AdaptorDrift Lend, Drift Perps
Jupiter AdaptorSpot via Jupiter Swap, Jupiter Lend
Trustful AdaptorCentralised Exchanges

1. Account Setup

const counterPartyTa = new PublicKey("protocol-token-account");

const [strategy] = PublicKey.findProgramAddressSync(
  [SEEDS.STRATEGY, counterPartyTa.toBuffer()],
  LENDING_ADAPTOR_PROGRAM_ID
);

const { vaultStrategyAuth } = client.findVaultStrategyAddresses(vault, strategy);

const vaultStrategyAssetAta = getAssociatedTokenAddressSync(
  vaultAssetMint,
  vaultStrategyAuth,
  true
);

let transactionIxs: TransactionInstruction[] = [];
try {
  await getAccount(connection, vaultStrategyAssetAta);
} catch {
  transactionIxs.push(
    createAssociatedTokenAccountInstruction(
      manager,
      vaultStrategyAssetAta,
      vaultStrategyAuth,
      vaultAssetMint
    )
  );
}
ATA behavior: ATAs created for strategy operations are not closed between deposit/withdraw cycles. The rent cost (~0.002 SOL per ATA) is a one-time cost paid by the manager.

2. Create Deposit Instruction

const depositAmount = new BN("1000000"); // Amount in smallest unit (e.g., 1 USDC = 1000000)

const depositIx = await client.createDepositStrategyIx(
  { depositAmount },
  {
    manager,
    vault,
    vaultAssetMint,
    assetTokenProgram: TOKEN_PROGRAM_ID,
    strategy,
    remainingAccounts: [
      { pubkey: counterPartyTa, isSigner: false, isWritable: true },
      { pubkey: protocolProgram, isSigner: false, isWritable: false },
      // Additional protocol-specific accounts...
    ],
  }
);

transactionIxs.push(depositIx);

3. Send Transaction

const txSig = await sendAndConfirmOptimisedTx(
  transactionIxs,
  "your-rpc-url",
  managerKp
);

Withdrawing Funds from Strategies

Protocol / AdaptorWithdraw Scripts
Kamino AdaptorKamino Vault, Kamino Lending Market
Drift AdaptorDrift Lend, Drift Perps
Jupiter AdaptorSpot via Jupiter Swap, Jupiter Lend
Trustful AdaptorCentralised Exchanges

1. Account Setup

const counterPartyTaAuth = await getAccount(
  connection,
  counterPartyTa,
  "confirmed"
).then((account) => account.owner);

let transactionIxs: TransactionInstruction[] = [];
try {
  await getAccount(connection, vaultStrategyAssetAta);
} catch {
  transactionIxs.push(
    createAssociatedTokenAccountInstruction(
      manager,
      vaultStrategyAssetAta,
      vaultStrategyAuth,
      vaultAssetMint
    )
  );
}

2. Create Withdrawal Instruction

const withdrawAmount = new BN("500000");

const withdrawIx = await client.createWithdrawStrategyIx(
  { withdrawAmount },
  {
    manager,
    vault,
    vaultAssetMint,
    assetTokenProgram: TOKEN_PROGRAM_ID,
    strategy,
    remainingAccounts: [
      { pubkey: counterPartyTaAuth, isSigner: false, isWritable: true },
      { pubkey: counterPartyTa, isSigner: false, isWritable: true },
      { pubkey: protocolProgram, isSigner: false, isWritable: false },
    ],
  }
);

transactionIxs.push(withdrawIx);

3. Send Transaction

const txSig = await sendAndConfirmOptimisedTx(
  transactionIxs,
  "your-rpc-url",
  managerKp
);

Best Practices

  • Keep idle reserves: Don’t deploy 100% of funds — leave a buffer for user withdrawals
  • Batch operations: Combine ATA creation and allocation in a single transaction
  • Monitor allocations: Track how funds are distributed across strategies
  • Automate: Use bots/scripts for regular rebalancing (see Running Bots & Scripts)

Troubleshooting

IssueSolution
Transaction too largeUse Lookup Tables
Insufficient fundsCheck idle balance, ensure enough SOL for gas
Authority errorVerify manager keypair matches vault’s manager
ATA not foundCreate the ATA before the allocation instruction
For additional support, refer to the Ranger Earn SDK documentation.