INSTRUCTIONS

cascade-vault on-chain reference.

Anchor program at FxausMaH3TWJkUeVPpLbYY55rVMztSKr6KtZ1jRFdrRw (devnet). Eight instructions covering vault lifecycle, deposit / withdraw, and cycle execution. Every state transition is enforced on-chain.

initialize_vault

Admin-only. Creates the Vault PDA, lp_mint, and asset_vault token account in one transaction.

pub fn initialize_vault(
    ctx: Context<InitializeVault>,
    config: VaultConfig,
) -> Result<()>;

// VaultConfig fields:
//   monitor_authority   Pubkey allowed to call deploy_lp / exit_lp / update_cluster_signal
//   stop_loss_bps       u16   (default 500 = 5%)
//   recovery_bps_min    u16   (default 150 = 1.5%)
//   timeout_slots       u64   (default 4500 = ~30 min)
//   max_deploy_pct      u16   (default 2000 = 20% of vault per deploy)
//   performance_fee_bps u16   (default 2000 = 20% of cycle profit)
//   withdraw_fee_bps    u16   (default 10 = 0.1%)

deposit

User-signed. Transfers asset into the vault, mints LP tokens pro-rata to NAV. Valid only when state is Idle.

pub fn deposit(ctx: Context<Deposit>, amount: u64) -> Result<()>;

// LP tokens minted:
//   first deposit:        amount (1:1 bootstrap)
//   subsequent deposits:  amount * lp_supply / total_assets

withdraw

User-signed. Burns LP tokens, returns asset pro-rata minus withdraw fee. Valid only when state is Idle.

pub fn withdraw(ctx: Context<Withdraw>, lp_amount: u64) -> Result<()>;

// Asset returned to depositor:
//   asset_to_return = lp_amount * total_assets / lp_supply
//   withdraw_fee    = asset_to_return * withdraw_fee_bps / 10000
//   net_to_depositor = asset_to_return - withdraw_fee
// withdraw_fee routes to fee_treasury.

update_cluster_signal

Monitor-only. Pushes a freshly computed cluster signal into vault state. The signal is checked again at deploy time.

pub fn update_cluster_signal(
    ctx: Context<UpdateCluster>,
    signal: ClusterSignal,
) -> Result<()>;

// Requires:
//   ctx.accounts.authority == vault.monitor_authority
//   signal.computed_at_slot >= current_slot - 5  (freshness)
//   signal.confidence >= 60                       (basic floor)

deploy_lp

Monitor-only. Transitions Idle → Deployed. In MVP this is a pure state transition; venue CPI (Raydium CLMM / Orca / Meteora) is stubbed pending follow-up.

pub fn deploy_lp(
    ctx: Context<DeployLp>,
    deploy_amount: u64,
    deploy_price: u64,
    range_low: u64,
    range_high: u64,
    venue: u8,
) -> Result<()>;

// Requires:
//   state == Idle
//   authority == vault.monitor_authority
//   deploy_amount <= total_assets * max_deploy_pct / 10000
//   cluster.confidence >= 80
//   deploy_price in [cluster.trigger_price_low, cluster.trigger_price_high]

exit_lp

Monitor-only. Transitions Deployed → Idle on recovery. Computes P&L, accrues performance fee.

pub fn exit_lp(ctx: Context<ExitLp>, exit_price: u64) -> Result<()>;

// Requires:
//   state == Deployed
//   authority == vault.monitor_authority
//   exit_price >= deploy_price * (1 + recovery_bps_min / 10000)

stop_loss_crank

Permissionless. Anyone can call when spot is 5% below deploy price.

pub fn stop_loss_crank(
    ctx: Context<StopLossCrank>,
    oracle_price: u64,
) -> Result<()>;

// Requires:
//   state == Deployed
//   oracle_price <= deploy_price * (1 - stop_loss_bps / 10000)
// MVP: oracle_price supplied by cranker. Production: read Pyth on-chain.

timeout_crank

Permissionless. Anyone can call after the configured slot count.

pub fn timeout_crank(
    ctx: Context<TimeoutCrank>,
    current_price: u64,
) -> Result<()>;

// Requires:
//   state == Deployed
//   clock.slot - deployed_at_slot >= timeout_slots

Events

Every state transition emits an event for off-chain indexing:

  • VaultInitialized
  • Deposited / Withdrawn
  • ClusterSignalUpdated
  • Deployed / PositionClosed (with reason: Recovery / StopLoss / Timeout)