Skip to main content

Strategy Vaults can also be created through the Strategy Vault manager frontend, which simplifies setup by offering a guided interface for configuring vault metadata, policies, roles, and other core vault parameters.
This guide walks through the vault setup workflow, from loading a vault to configuring policies and executing strategy operations, as well as managing withdrawals.

Exponent SDK Installation

yarn add @exponent-labs/exponent-sdk

Strategy Vault Setup

import { ExponentVault } from "@exponent-labs/exponent-sdk";
import {
  Connection,
  PublicKey,
  Transaction,
  sendAndConfirmTransaction,
  Keypair,
} from "@solana/web3.js";
const connection = new Connection("https://api.mainnet-beta.solana.com");
const wallet = Keypair.fromSecretKey(/* your keypair */);
const vaultAddress = new PublicKey("...");

// Load the vault — fetches on-chain state and all token entries
const vault = await ExponentVault.load({ connection, address: vaultAddress });

Vault Creation

Strategy Vaults are created by the vault manager using ExponentVault.ixInitializeVault. This sets up the vault PDA, mints initial LP tokens, and creates the Squads smart account for policy-gated execution. See Initialize Vault for the full parameter reference.

Loading a Vault

import { ExponentVault } from "@exponent-labs/exponent-sdk";
import { Connection, PublicKey } from "@solana/web3.js";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const vaultAddress = new PublicKey("...");

const vault = await ExponentVault.load({ connection, address: vaultAddress });

// Access vault state
const squadsVault = vault.state.squadsVault;     // Squads smart account PDA
const tokenEntries = vault.state.tokenEntries;    // Accepted deposit tokens
const aumInBase = vault.state.financials.aumInBase;

Configuring Policies

Before the vault can execute any strategy operations, the manager must add policies that authorize specific interactions. Each policy defines which programs, instructions, and accounts the vault is permitted to interact with. Policies are added using vault.ixWrapperAddPolicy. This instruction enforces that the vault has zero AUM — policies must be configured before any deposits are accepted.
Once the vault holds depositor funds (AUM > 0), policy changes must go through a governance proposal using PolicyAction. See Governance for the proposal flow.
import {
  ExponentVault,
  createKaminoPolicy,
  KAMINO_MARKETS,
  KAMINO_RESERVES,
  KaminoMarket,
} from "@exponent-labs/exponent-sdk";
import { Connection, PublicKey, Transaction, sendAndConfirmTransaction } from "@solana/web3.js";

const connection = new Connection("https://api.mainnet-beta.solana.com");
const vault = await ExponentVault.load({ connection, address: vaultAddress });

const policyConfig = createKaminoPolicy({
  allowedDepositMints: [KAMINO_RESERVES[KaminoMarket.MAIN]["USDC"].mint],
  actions: ["deposit", "withdraw"],
  allowedLendingMarkets: [KAMINO_MARKETS[KaminoMarket.MAIN]],
});

const ix = vault.ixWrapperAddPolicy({
  payer: managerWallet.publicKey,
  policyConfig,
  squadsProgram,
  nextPolicySeed,
  nextTransactionIndex,
});

const tx = new Transaction().add(ix);
await sendAndConfirmTransaction(connection, tx, [managerWallet]);

Available Policy Builders

The SDK provides policy builders for each supported integration:
BuilderIntegrationDescription
createCorePolicyExponent CoreStrip and merge base tokens into PT + YT
createOrderbookPolicyExponent OrderbookPost offers, market orders, remove offers, withdraw funds
createClmmPolicyExponent CLMMProvide liquidity, trade PT/YT, claim farm emissions
createKaminoPolicyKamino LendingDeposit, withdraw, borrow, and repay on Kamino reserves
createTitanSwapPolicyTitanSwap tokens through the Titan DEX aggregator
See Policy Builders for detailed parameters and examples for each builder.
Policies are enforced onchain. Any transaction that does not match an approved policy is rejected. Ensure the correct policies are in place before attempting strategy operations.

Executing Strategy Operations

Strategy operations use a two-step pattern:
  1. Build instruction descriptors using a protocol-specific action builder (kaminoAction, orderbookAction, coreAction, clmmAction, titanAction). Each builder returns a lightweight descriptor — it does not execute anything on its own.
  2. Wrap in a sync transaction by passing the descriptors to createVaultSyncTransaction, which resolves all required accounts, builds the Squads sync transaction, and returns the final instructions to send.
import {
  kaminoAction,
  createVaultSyncTransaction,
  KaminoMarket,
} from "@exponent-labs/exponent-sdk";
import { BN } from "@coral-xyz/anchor";

// Step 1: Build an instruction descriptor
const depositDescriptor = kaminoAction.deposit(KaminoMarket.MAIN, "USDC", new BN(100_000_000));

// Step 2: Wrap in a sync transaction
const { preInstructions, instruction, postInstructions } =
  await createVaultSyncTransaction({
    instructions: [depositDescriptor],
    owner: vaultPda,
    connection,
    policyPda,
    vaultPda,
    signer: wallet.publicKey,
    vaultAddress,
  });

// Step 3: Send
const tx = new Transaction().add(...preInstructions, instruction, ...postInstructions);
await sendAndConfirmTransaction(connection, tx, [wallet]);

Action Builders

Each integration has its own action builder namespace:
Action BuilderIntegrationExample
coreActionExponent CorecoreAction.strip({ vault, amountBase })
orderbookActionExponent OrderbookorderbookAction.postOffer({ orderbook, direction, priceApy, amount, offerIdx })
clmmActionExponent CLMMclmmAction.buyPt({ market, amountSy, outConstraint })
kaminoActionKamino LendingkaminoAction.deposit(KaminoMarket.MAIN, "USDC", new BN(100_000_000))
titanActionTitantitanAction.swap({ instruction })
For Titan swaps, the instruction descriptor wraps a pre-built TransactionInstruction obtained from getTitanQuote. See Titan Instructions for the full flow.
See the individual instruction pages for full usage and parameter details:

Managing Withdrawals

When depositors queue withdrawals, the vault manager fills them from available liquidity:
  1. Queue — A depositor calls ixQueueWithdrawal to lock LP tokens and create a withdrawal request.
  2. Fill — The vault manager calls fillWithdrawal to associate underlying tokens with the pending request. LP tokens are burned at this step.
  3. Execute — The depositor calls ixExecuteWithdrawal to receive their underlying tokens.
See Moving Capital In/Out for the full depositor-facing flow.