Skip to main content

Setup

All examples use this shared setup:
import { Connection, Keypair, PublicKey } from "@solana/web3.js";
import {
  SymmetryCore,
  KeeperMonitor,
  RebalanceHandler,
  getJupTokenLedgerAndSwapInstructions,
  getSwapPairs,
  isRebalanceRequired,
} from "@symmetry-hq/sdk";

function createWallet(keypair: Keypair) {
  return {
    publicKey: keypair.publicKey,
    signTransaction: async <T>(tx: T): Promise<T> => {
      (tx as any).sign([keypair]);
      return tx;
    },
    signAllTransactions: async <T>(txs: T[]): Promise<T[]> => {
      txs.forEach((tx: any) => tx.sign([keypair]));
      return txs;
    },
    payer: keypair,
  };
}

const connection = new Connection("https://api.mainnet-beta.solana.com");
const keypair = Keypair.fromSecretKey(/* your secret key bytes */);
const wallet = createWallet(keypair);

const sdk = new SymmetryCore({
  connection,
  network: "mainnet",
  priorityFee: 50_000,
});

Create a Vault with Tokens

Creates a 2-token vault with SOL (50%) and USDC (50%). The metadata_uri must point to a JSON file with the vault’s metadata. The JSON should contain at minimum name, symbol, description, image, and cover fields. You can add any extra fields (website, social links, etc.) for your own integrations to consume. Example metadata JSON at the URI:
{
  "name": "Diversified Index",
  "symbol": "DIVX",
  "description": "A diversified vault tracking SOL, USDC, and other tokens.",
  "image": "https://arweave.net/your-token-image-url",
  "cover": "https://arweave.net/your-cover-image-url"
}
const vaultResult: VaultCreationTx = await sdk.createVaultTx({
  creator: wallet.publicKey.toBase58(),
  start_price: "1.0",
  name: "Diversified Index",
  symbol: "DIVX",
  metadata_uri: "https://arweave.net/your-metadata-json",  // URL to JSON with name, symbol, description, image, cover
  host_platform_params: {
    host_pubkey: wallet.publicKey.toBase58(),
    host_deposit_fee_bps: 10,
    host_withdraw_fee_bps: 10,
    host_management_fee_bps: 0,    // currently disabled in global config
    host_performance_fee_bps: 0,   // currently disabled in global config
  },
});

console.log("Vault mint:", vaultResult.mint);
console.log("Vault account:", vaultResult.vault);

await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: vaultResult,
  wallet,
});

// Add SOL with Pyth oracle (find price feed IDs at https://docs.pyth.network/price-feeds/core/price-feeds/price-feed-ids)
const addSolTx: TxPayloadBatchSequence = await sdk.addOrEditTokenTx(
  { vault: vaultResult.vault, manager: wallet.publicKey.toBase58() },
  {
    token_mint: "So11111111111111111111111111111111111111112",
    active: true,
    min_oracles_thresh: 1,
    min_conf_bps: 10,
    conf_thresh_bps: 200,
    conf_multiplier: 1.0,
    oracles: [{
      oracle_type: "pyth",
      account_lut_id: 0,
      account_lut_index: 0,
      account: "7UVimffxr9ow1uXYxsr4LHAcV58mLzhmwaeKvJ1pjLiE",
      weight_bps: 10000,
      is_required: true,
      conf_thresh_bps: 200,
      volatility_thresh_bps: 200,
      max_slippage_bps: 1000,
      min_liquidity: 0,
      staleness_thresh: 120,
      staleness_conf_rate_bps: 50,
      token_decimals: 9,
      twap_seconds_ago: 0,
      twap_secondary_seconds_ago: 0,
      quote_token: "usd",
    }],
  }
);
await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: addSolTx, wallet });

const addUsdcTx: TxPayloadBatchSequence = await sdk.addOrEditTokenTx(
  { vault: vaultResult.vault, manager: wallet.publicKey.toBase58() },
  {
    token_mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
    active: true,
    min_oracles_thresh: 1,
    min_conf_bps: 10,
    conf_thresh_bps: 200,
    conf_multiplier: 1.0,
    oracles: [{
      oracle_type: "pyth",
      account_lut_id: 0,
      account_lut_index: 0,
      account: "Dpw1EAVrSB1ibxiDQyTAW6Zip3J4Btk2x4SgApQCeFbX",
      weight_bps: 10000,
      is_required: true,
      conf_thresh_bps: 200,
      volatility_thresh_bps: 200,
      max_slippage_bps: 1000,
      min_liquidity: 0,
      staleness_thresh: 120,
      staleness_conf_rate_bps: 50,
      token_decimals: 6,
      twap_seconds_ago: 0,
      twap_secondary_seconds_ago: 0,
      quote_token: "usd",
    }],
  }
);
await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: addUsdcTx, wallet });

const weightsTx: TxPayloadBatchSequence = await sdk.updateWeightsTx(
  { vault: vaultResult.vault, manager: wallet.publicKey.toBase58() },
  {
    token_weights: [
      { mint: "So11111111111111111111111111111111111111112", weight_bps: 5000 },
      { mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", weight_bps: 5000 },
    ],
  }
);
await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: weightsTx, wallet });

Deposit into a Vault

const VAULT_MINT = "<VAULT_TOKEN_MINT>";

// Amounts are raw (smallest units): SOL = 9 decimals, USDC = 6 decimals
const buyTx: TxPayloadBatchSequence = await sdk.buyVaultTx({
  buyer: wallet.publicKey.toBase58(),
  vault_mint: VAULT_MINT,
  contributions: [
    { mint: "So11111111111111111111111111111111111111112", amount: 500_000_000 },   // 0.5 SOL
    { mint: "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v", amount: 50_000_000 }, // 50 USDC
  ],
  rebalance_slippage_bps: 100,
  per_trade_rebalance_slippage_bps: 100,
});

await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: buyTx,
  wallet,
});

const lockTx = await sdk.lockDepositsTx({
  buyer: wallet.publicKey.toBase58(),
  vault_mint: VAULT_MINT,
});

await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: lockTx,
  wallet,
});

Standard Withdrawal

const sellTx: TxPayloadBatchSequence = await sdk.sellVaultTx({
  seller: wallet.publicKey.toBase58(),
  vault_mint: "<VAULT_TOKEN_MINT>",
  withdraw_amount: 1_000_000,  // raw vault token amount to burn
  keep_tokens: [],
  rebalance_slippage_bps: 100,
  per_trade_rebalance_slippage_bps: 100,
});

await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: sellTx,
  wallet,
});

Withdraw and Keep Specific Tokens

const sellTx: TxPayloadBatchSequence = await sdk.sellVaultTx({
  seller: wallet.publicKey.toBase58(),
  vault_mint: VAULT_MINT,
  withdraw_amount: 1_000_000,
  keep_tokens: ["So11111111111111111111111111111111111111112"],
  rebalance_slippage_bps: 100,
  per_trade_rebalance_slippage_bps: 100,
});

Fast Withdrawal

Pass every mint from vault.composition to skip auctions:
const vault = await sdk.fetchVault("<VAULT_PUBKEY>");
const allMints = vault.formatted!.composition.map(asset => asset.mint);

const sellTx = await sdk.sellVaultTx({
  seller: wallet.publicKey.toBase58(),
  vault_mint: vault.formatted!.mint,
  withdraw_amount: 1_000_000,
  keep_tokens: allMints,
  rebalance_slippage_bps: 100,
  per_trade_rebalance_slippage_bps: 100,
});
await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: sellTx, wallet });

const redeemTx = await sdk.redeemTokensTx({
  keeper: wallet.publicKey.toBase58(),
  rebalance_intent: "<REBALANCE_INTENT_PUBKEY>",
});
await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: redeemTx, wallet });

Read Vault Data

let vault = await sdk.fetchVault("<VAULT_PUBKEY>");
vault = await sdk.loadVaultPrice(vault);

const info: FormattedVault = vault.formatted!;
console.log("Name:", info.name);
console.log("Symbol:", info.symbol);
console.log("TVL:", vault.tvl?.toString());
console.log("Token Price:", vault.price?.toString());
console.log("Supply:", info.supply_outstanding);
console.log("Creator:", info.creator);
console.log("Host:", info.host);

console.log("\nComposition:");
for (const asset of info.composition) {
  if (!asset.active) continue;
  console.log(`  ${asset.mint}: weight=${asset.weight / 100}%, amount=${asset.amount}`);
}

console.log("\nFee Settings:");
const fees = info.fee_settings;
console.log(`  Creator deposit: ${fees.creator_deposit_fee_bps / 100}%`);
console.log(`  Creator withdrawal: ${fees.creator_withdraw_fee_bps / 100}%`);
console.log(`  Creator management: ${fees.creator_management_fee_bps / 100}%`);
console.log(`  Creator performance: ${fees.creator_performance_fee_bps / 100}%`);

console.log("\nAutomation:", info.automation_settings.enabled);
console.log("Deposits enabled:", info.deposits_settings.enabled);

List All Vaults with Prices

const vaults = await sdk.fetchAllVaults();

for (const vault of vaults) {
  try {
    const priced = await sdk.loadVaultPrice(vault);
    console.log(
      `${priced.formatted!.name} (${priced.formatted!.symbol})` +
      ` | TVL: $${priced.tvl?.toFixed(2)}` +
      ` | Price: $${priced.price?.toFixed(6)}` +
      ` | Tokens: ${priced.formatted!.composition.filter(a => a.active).length}`
    );
  } catch (e) {
    console.log(`${vault.formatted!.name}: price load failed`);
  }
}

Update Vault Fees

const tx = await sdk.editFeesTx(
  {
    vault: "<VAULT_PUBKEY>",
    manager: wallet.publicKey.toBase58(),
  },
  {
    creator_deposit_fee_bps: 25,
    creator_withdraw_fee_bps: 25,
    creator_management_fee_bps: 200,   // currently disabled in global config
    creator_performance_fee_bps: 1000, // currently disabled in global config
    managers_deposit_fee_bps: 0,
    managers_withdraw_fee_bps: 0,
    managers_management_fee_bps: 0,    // currently disabled in global config
    managers_performance_fee_bps: 0,   // currently disabled in global config
    vault_deposit_fee_bps: 10,
    vault_withdraw_fee_bps: 10,
    modification_delay: 86400,
  }
);

await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet });

Run a Keeper Bot

const keeper = new KeeperMonitor({
  wallet,
  connection,
  network: "mainnet",
  jupiterApiKey: "<YOUR_JUPITER_API_KEY>",
  maxAllowedAccounts: 64,
  priorityFee: 50_000,
  simulateTransactions: false,
});

// Option A: Run indefinitely
while (true) {
  try {
    await keeper.update();
  } catch (e) {
    console.error("Keeper update error:", e);
  }
  await new Promise(r => setTimeout(r, 10_000));
}

// Option B: Run for a fixed duration
await keeper.run(600);

Handle a Single Rebalance

await RebalanceHandler.run({
  intentPubkey: new PublicKey("<REBALANCE_INTENT_PUBKEY>"),
  wallet,
  connection,
  network: "mainnet",
  jupiterApiKey: "<JUP_API_KEY>",
  maxAllowedAccounts: 64,
  priorityFee: 50_000,
});

Claim Fees

const claimTx = await sdk.withdrawVaultFeesTx({
  claimer: wallet.publicKey.toBase58(),
  vault: "<VAULT_PUBKEY>",
});

await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: claimTx,
  wallet,
});

Check and Trigger Rebalance

const vault = await sdk.fetchVault("<VAULT_PUBKEY>");
const pricedVault = await sdk.loadVaultPrice(vault);
const needsRebalance = await isRebalanceRequired(pricedVault, connection);

if (needsRebalance) {
  const tx = await sdk.rebalanceVaultTx({
    keeper: wallet.publicKey.toBase58(),
    vault_mint: vault.mint.toBase58(),
    rebalance_slippage_bps: 100,
    per_trade_rebalance_slippage_bps: 100,
  });

  await sdk.signAndSendTxPayloadBatchSequence({
    txPayloadBatchSequence: tx,
    wallet,
  });
}

Set Up Manager Authorities

const tx = await sdk.editManagersTx(
  {
    vault: "<VAULT_PUBKEY>",
    manager: wallet.publicKey.toBase58(),
  },
  {
    managers: [
      {
        pubkey: "<MANAGER_1_PUBKEY>",
        fee_split_weight_bps: 6000,
        authorities: {
          managers: true,
          fees: true,
          schedule: true,
          automation: true,
          lp: true,
          metadata: true,
          deposits: true,
          force_rebalance: true,
          custom_rebalance: true,
          add_token: true,
          update_weights: true,
          make_direct_swap: true,
        },
      },
      {
        pubkey: "<MANAGER_2_PUBKEY>",
        fee_split_weight_bps: 4000,
        authorities: {
          managers: false,
          fees: false,
          schedule: false,
          automation: false,
          lp: false,
          metadata: true,
          deposits: true,
          force_rebalance: false,
          custom_rebalance: false,
          add_token: false,
          update_weights: true,
          make_direct_swap: false,
        },
      },
    ],
    modification_delay: 172800,
  }
);

await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet });

Enable Automation

const tx = await sdk.editAutomationTx(
  {
    vault: "<VAULT_PUBKEY>",
    manager: wallet.publicKey.toBase58(),
  },
  {
    enabled: true,
    rebalance_slippage_threshold_bps: 100,
    per_trade_rebalance_slippage_threshold_bps: 100,
    rebalance_activation_threshold_abs_bps: 500,
    rebalance_activation_threshold_rel_bps: 1000,
    rebalance_activation_cooldown: 3600,
    modification_delay: 86400,
  }
);

await sdk.signAndSendTxPayloadBatchSequence({ txPayloadBatchSequence: tx, wallet });

Monitor Rebalance Intents for a Vault

const VAULT_PUBKEY: string = "<VAULT_PUBKEY>";

const rebalanceIntents: UIRebalanceIntent[] = await sdk.fetchVaultRebalanceIntents(VAULT_PUBKEY);

for (const ri of rebalanceIntents) {
  const data: FormattedRebalanceIntent = ri.formatted_data;
  console.log(`Intent: ${data.pubkey}`);
  console.log(`  Type: ${data.rebalance_type}`);
  console.log(`  Action: ${data.current_action}`);
  console.log(`  Owner: ${data.owner}`);

  if (ri.deposit_data) {
    console.log(`  Deposits:`);
    for (const token of ri.deposit_data.tokens) {
      console.log(`    ${token.mint}: ${token.amount}`);
    }
  }

  if (ri.auction_data) {
    console.log(`  Auction stages:`);
    for (const stage of ri.auction_data.auction_stages) {
      console.log(`    ${new Date(stage.start_time * 1000).toISOString()} - ${new Date(stage.end_time * 1000).toISOString()}`);
    }
  }
}

Add Bounty to a Vault

const tx = await sdk.addBountyTx({
  keeper: wallet.publicKey.toBase58(),
  vault: "<VAULT_PUBKEY>",
  amount: 100_000_000,
});

await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: tx,
  wallet,
});

Build a Portfolio Tracker

async function getPortfolio(ownerPubkey: string) {
  const allVaults = await sdk.fetchAllVaults();
  const portfolio = [];

  for (const vault of allVaults) {
    const pricedVault = await sdk.loadVaultPrice(vault);
    const vaultMint = pricedVault.mint.toBase58();

    portfolio.push({
      name: pricedVault.formatted!.name,
      symbol: pricedVault.formatted!.symbol,
      mint: vaultMint,
      price: pricedVault.price?.toNumber(),
      tvl: pricedVault.tvl?.toNumber(),
      composition: pricedVault.formatted!.composition
        .filter(a => a.active)
        .map(a => ({
          mint: a.mint,
          weight: a.weight / 100,
          amount: a.amount,
        })),
    });
  }

  return portfolio;
}

Common Patterns

Error Handling

try {
  const tx = await sdk.buyVaultTx({ /* ... */ });
  const sigs = await sdk.signAndSendTxPayloadBatchSequence({
    txPayloadBatchSequence: tx,
    wallet,
  });
  console.log("Success:", sigs);
} catch (error) {
  console.error("Transaction failed:", error);
}

Preflight Mode

simulateTransactions: true enables a preflight-style send path in the SDK — it does not perform offline-only simulation. Transactions are still sent to the network. Do not use on production keys if you expect zero chain side-effects.
const sigs = await sdk.signAndSendTxPayloadBatchSequence({
  txPayloadBatchSequence: tx,
  wallet,
  simulateTransactions: true,
});

Working with Amounts

Token amounts are always in the smallest unit (lamports):
TokenDecimals1 token =
SOL91,000,000,000 lamports
USDC61,000,000
GeneralNrawAmount = humanAmount * 10^N
This applies to contributions[].amount, withdraw_amount, amount_in, amount_out, bounty amount, min_bounty_amount, and max_bounty_amount.

Vault Mint vs Vault Account

Two addresses are associated with each vault:
  • Vault mint (vault.mint): The SPL token mint of the vault token. Used in buyVaultTx, sellVaultTx, rebalanceVaultTx.
  • Vault account (vault.ownAddress or vault.formatted.pubkey): The on-chain state account. Used in fetchVault, addOrEditTokenTx, editFeesTx, etc.
To go from mint → account: getVaultState(mintPubkey) (PDA derivation). To go from account → mint: read vault.mint from the fetched vault.