Skip to main content

Overview

Vaults are the core abstraction in GLAM for managing on-chain assets. Each vault is controlled by a state account and has an associated vault PDA that holds all assets.

Vault structure

Every GLAM vault consists of several key components:
// Derived PDAs for a vault
const statePda = client.statePda;           // State account (vault metadata)
const vaultPda = client.vaultPda;           // Holds all vault assets
const mintPda = client.mintPda;             // Share token (if tokenized)
const escrowPda = client.escrowPda;         // Escrow for subscriptions
const requestQueuePda = client.requestQueuePda; // Pending requests
All PDAs are automatically derived from statePda when you connect a client to a vault.

State account

The state account (StateAccount) stores all vault configuration and metadata:
type StateAccount = {
  accountType: StateAccountType;             // Vault type
  name: number[];                            // Vault name (as bytes)
  uri: string;                               // Metadata URI
  enabled: boolean;                          // Vault enabled/disabled
  
  owner: PublicKey;                          // Vault owner
  baseAssetMint: PublicKey;                  // Base denomination (e.g., USDC)
  baseAssetTokenProgram: number;             // Token program (0=SPL, 1=Token2022)
  
  assets: PublicKey[];                       // Allowed assets
  borrowable: PublicKey[];                   // Borrowable assets
  reduceOnly: boolean;                       // Reduce-only mode
  anyLst: boolean;                           // Allow any LST
  
  delegateAcls: DelegateAcl[];              // Delegate permissions
  integrationAcls: IntegrationAcl[];        // Protocol permissions
  
  timelockDuration: number;                  // Timelock in seconds
  params: StateParams[];                     // Serialized parameters
};

StateModel

The SDK provides an enriched StateModel class that combines multiple on-chain accounts:
const stateModel = await client.fetchStateModel();

// Access vault properties
console.log(stateModel.nameStr);           // Human-readable name
console.log(stateModel.productType);       // "Tokenized_vault", etc.
console.log(stateModel.launchDate);        // ISO date string
console.log(stateModel.mintModel);         // Mint configuration (if tokenized)

Key methods

// Build StateModel from on-chain accounts
static fromOnchainAccounts(
  statePda: PublicKey,
  stateAccount: StateAccount,
  staging: boolean,
  glamMint?: Mint,                           // Optional for tokenized vaults
  requestQueue?: RequestQueue                // Optional for tokenized vaults
): StateModel

Vault operations

The VaultClient provides methods for common vault operations:

Depositing assets

// Deposit SOL (wrapped to wSOL by default)
await client.vault.depositSol(
  1_000_000_000,  // 1 SOL in lamports
  true,           // Wrap to wSOL
  { signer: walletPublicKey }
);

// Deposit tokens
await client.vault.deposit(
  usdcMint,
  1_000_000,      // 1 USDC (6 decimals)
  { signer: walletPublicKey }
);

Transferring assets

// Transfer SOL from vault
await client.vault.systemTransfer(
  500_000_000,    // 0.5 SOL
  recipientPubkey,
  { signer: managerPublicKey }
);

// Transfer tokens from vault
await client.vault.tokenTransfer(
  usdcMint,
  1_000_000,      // 1 USDC
  recipientPubkey,
  { signer: managerPublicKey }
);

Wrapping and unwrapping SOL

// Wrap vault SOL to wSOL
await client.vault.wrap(
  1_000_000_000,  // 1 SOL
  { signer: managerPublicKey }
);

// Unwrap wSOL back to SOL
await client.vault.unwrap(
  { signer: managerPublicKey }
);

// Conditionally wrap SOL if needed
const wrapIxs = await client.vault.maybeWrapSol(
  2_000_000_000,  // Need 2 SOL worth of wSOL
  managerPublicKey
);
// Returns empty array if sufficient wSOL already exists

Managing token accounts

// Close multiple token accounts (reclaim rent)
await client.vault.closeTokenAccounts(
  [tokenAccount1, tokenAccount2],
  { signer: managerPublicKey }
);

Querying vault balances

The BaseClient provides methods to query vault holdings:
// Get vault SOL balance
const solBalance = await client.getVaultBalance();  // Returns SOL
const lamports = await client.getVaultLamports();   // Returns lamports

// Get vault token balance
const { amount, uiAmount } = await client.getVaultTokenBalance(usdcMint);
console.log(`Vault has ${uiAmount} USDC`);

// Get all vault token accounts
const { balanceLamports, uiAmount, tokenAccounts } = 
  await client.getSolAndTokenBalances(client.vaultPda);

State management

The StateClient handles vault configuration:

Creating a vault

import { stringToChars } from '@glam/sdk';

const signature = await client.state.initialize({
  accountType: StateAccountType.TOKENIZED_VAULT,
  name: stringToChars("My Fund", 32),
  baseAssetMint: USDC,
  assets: [USDC, WSOL],
  portfolioManagerName: stringToChars("ACME Capital", 64),
}, { signer: ownerPublicKey });

Updating vault configuration

// Update allowed assets
await client.state.update({
  assets: [USDC, WSOL, JUP],
}, { signer: ownerPublicKey });

// Update owner
await client.state.update({
  owner: newOwnerPublicKey,
}, { signer: currentOwnerPublicKey });
If timelock is enabled, updates are staged and require a second transaction after the timelock period expires.

Extending state account size

// Add 1000 bytes to state account
await client.state.extend(
  1000,
  { signer: ownerPublicKey }
);

Closing a vault

// Close the state account (vault must be empty)
await client.state.close(
  { signer: ownerPublicKey }
);

Getting vault addresses

The client provides convenient getters for associated token accounts:
// Get vault's token account for any mint
const vaultUsdc = client.getVaultAta(USDC);
const vaultWsol = client.getVaultAta(WSOL);

// Get user's vault share token account
const userSharesAta = client.getMintAta(userPublicKey);

// Get any ATA
const ata = client.getAta(
  mintPublicKey,
  ownerPublicKey,
  TOKEN_PROGRAM_ID
);

Transaction options

All vault operations accept TxOptions for advanced configuration:
type TxOptions = {
  signer?: PublicKey;                        // Transaction signer
  computeUnitLimit?: number;                 // CU limit (auto-calculated)
  getPriorityFeeMicroLamports?: (tx: VersionedTransaction) => Promise<number>;
  maxFeeLamports?: number;                   // Max priority fee
  useMaxFee?: boolean;                       // Use max fee instead of calculating
  preInstructions?: TransactionInstruction[];  // Added before main instructions
  postInstructions?: TransactionInstruction[]; // Added after main instructions
  lookupTables?: PublicKey[] | AddressLookupTableAccount[];
  simulate?: boolean;                        // Throw on simulation error
};

Using transaction options

// Add memo and set priority fee
await client.vault.deposit(usdcMint, 1_000_000, {
  signer: walletPublicKey,
  postInstructions: [memoInstruction],
  getPriorityFeeMicroLamports: async (tx) => {
    // Calculate priority fee based on network conditions
    return 10_000; // 0.00001 SOL
  },
  simulate: true  // Throw error if simulation fails
});

Working with transaction builders

For advanced use cases, you can access the underlying transaction builders:
// Build instructions without sending
const depositIxs = await client.vault.txBuilder.depositIxs(
  usdcMint,
  1_000_000,
  walletPublicKey
);

// Build a versioned transaction
const vTx = await client.vault.txBuilder.depositTx(
  usdcMint,
  1_000_000,
  { signer: walletPublicKey }
);

// Sign and send manually
const signature = await client.sendAndConfirm(vTx);

Next steps

Clients

Learn about the client architecture

Access control

Configure permissions and delegates

Integrations

Explore DeFi protocol integrations

Build docs developers (and LLMs) love