xVault Docs
Protocol

Architecture

How the dapp, SDK, keeper, indexer, and Anchor programs fit together.

xVault splits into three layers: a user-facing dapp, a set of off-chain services (keeper + indexer), and three Anchor programs on Solana.

Component map

                  ┌──────────────┐
                  │   Next.js    │
                  │   dapp       │
                  └──────┬───────┘
                         │ SDK
             ┌───────────┴────────────┐
             │                        │
             ▼                        ▼
     ┌───────────────┐        ┌───────────────┐
     │  Anchor       │        │  xStocks API  │
     │  programs     │        │  (Backed)     │
     │  vault /      │        └───────┬───────┘
     │  oracle /     │                │
     │  rewards      │        ┌───────┴───────┐
     └───────┬───────┘        │   Jupiter     │
             │                └───────┬───────┘
             │                        │
             ▼                        ▼
       ┌───────────────────────────────────┐
       │  Keeper service                   │
       │  — NAV push · drift scan · RFQ    │
       │    exec · corp-action watch ·     │
       │    epoch distribution             │
       └────────────┬──────────────────────┘
                    │ events

             ┌───────────────┐
             │   Indexer     │
             │  (Postgres)   │
             └───────────────┘

Components

LayerResponsibility
apps/webDapp, wallet flows, geofence and terms gating
apps/docsPublic and operator-facing documentation
packages/sdkTyped client bindings generated from the Anchor IDLs
packages/xstocks-clientTyped wrapper around the xStocks public and authenticated APIs
services/keeperNAV pushes, drift scans, rebalance execution, corp-action response
services/indexerHelius-webhook-driven event indexer writing to Postgres
programs/vaultDeposits, withdrawals, rebalance bookkeeping, fees, pause controls
programs/oracleKeeper-pushed NAV snapshots with multiplier-aware entries
programs/rewards$VLT staking and epoch reward claims

On-chain programs

vault

PDAs:

  • vault_state seeds ["vault", sku_id] — target weights, last NAV, fee config
  • vault_share_mint seeds ["share", sku_id] — Token-2022 mint, vault PDA is authority
  • holding_ata(asset) seeds ["holding", sku_id, mint] — vault's ATA per asset

Instructions:

IxAuthorityNotes
init_vault(args)admin multisigOne-shot config for SKU, holdings, fee params, keeper + admin authorities.
deposit(raw_amount, min_shares_out)userDeposits USDC, mints shares from oracle NAV.
withdraw_in_kind(shares)userBurns shares, returns pro-rata raw xStock amounts.
withdraw_usdc(shares, max_slip_bps)userBurns shares, pays USDC from cash buffer; charges 0.05% fee.
rebalance_leg(args)keeperValidates and books rebalance execution against expected raw deltas.
collect_mgmt_fee()permissionlessStreams 0.2%/yr to treasury.
collect_perf_fee()permissionlessHWM-based performance fee.
set_paused(paused)admin multisigEmergency circuit breaker.
update_weights(new_weights[])admin multisigUpdates target weights (behind 48h timelock).

State guards (every ix):

  • asset.status == Active
  • nav_age < MAX_STALE_SECS
  • multiplier == expected_multiplier (re-verified before any transfer)
  • In rebalance_leg: sibling-ix introspection confirms incoming/outgoing transfers match the quoted mint and raw amount.
  • Both Token and Token-2022 program IDs are supported per asset, driven by token_programs[] at init_vault.

oracle

  • nav_snapshot PDA per SKU with up to 20 entries — mint, price_usd_1e8, multiplier_num, flags, updated_slot.
  • Primary price source: GET /v2/oracles/{symbol} (Backed's onchain oracle feed).
  • Secondary: GET /public/assets/{symbol}/price-data (indicative quote only).
  • Tertiary: Pyth equity feed for overlapping tickers.
  • Keeper signs via ed25519; program verifies the signer is in the authorized keeper set.
  • Staleness limits: MAX_STALE_SECS = 60 (market hours), 300 (off-hours). NAV-sensitive ix revert with StaleNav if exceeded.

rewards

  • stake(amount, tier) — locks $VLT for 30/90/180d. Pro-rata weighting, no boost multipliers.
  • distribute_epoch(epoch_id, merkle_root) — keeper posts the Merkle root of pro-rata xStock payouts.
  • claim(proof, amounts[]) — user claims their share per epoch.

Off-chain services

Keeper

Stack: Node 22 + TypeScript, Anchor client, Bull queue on Redis, Postgres.

JobPurposeCadence
nav-pushFetch prices + multipliers, write oracle PDA15 s market hours / 60 s off-hours
drift-scanCompute per-vault drift, enqueue rebalance jobs60 s
rebalance-execRequest xChange RFQ or Jupiter quote, co-sign, submitTriggered by drift
corp-action-watchPoll /public/corporate-actions/upcoming, alert on severe eventsHourly
epoch-distributorSweep Pump.fun fees, build Merkle distribution, post epoch rootWeekly (Sunday 00:00 UTC)
collect-mgmt-feePermissionless trigger for management fee collectionDaily
deposit-routerListen for deposit events, buy basket, validate via rebalance_legEvent-driven
withdrawal-routerListen for USDC withdrawal requests, sell xStocks, refill cash bufferEvent-driven

HA: leader election via Redis Redlock; two replicas in separate AZs.

Indexer

  • Subscribes to program logs via Helius Raw webhooks (1 credit / event; not Enhanced at 100 credits each).
  • Decodes events locally with the Anchor IDL; writes normalized rows to Postgres (deposits, withdrawals, rebalances, fees, stakes, claims).
  • Powers UI analytics (TVL, APR, fee accruals).
  • One webhook per program (3 total), fits within the Free-tier 5-webhook cap.

Data flow — a deposit

User              Web           SDK            Program         xStocks API     Keeper
 │  click deposit  │              │               │                │             │
 │  (USDC)         │              │               │                │             │
 │────────────────▶│ getNavQuote()│               │                │             │
 │                 │─────────────▶│ read oracle   │                │             │
 │                 │              │──────────────▶│                │             │
 │                 │◀─────────────┤               │                │             │
 │  sign tx        │              │               │                │             │
 │────────────────▶│ deposit()    │               │                │             │
 │                 │──────────────────────────────▶│               │             │
 │                 │              │               │ USDC transfer  │             │
 │                 │              │               │ mint shares    │             │
 │                 │              │               │ emit event     │             │
 │                 │              │               │──────────────────────────────▶
 │                 │              │               │                │             │ reads
 │                 │              │               │                │             │ deposit
 │                 │              │               │                │             │ event
 │                 │              │               │ rebalance_leg ◀────────────────
 │                 │              │               │ buy basket via xChange/Jupiter

Infrastructure posture

We target Helius Developer tier for v1 mainnet (5/s sendTransaction, 50 RPC/s, 10 DAS/s, Enhanced Websockets, 50 webhooks). The keeper enforces credit-budget rules:

  • Never loop Enhanced Transactions (use raw getSignaturesForAddress + getTransaction + IDL decode).
  • Never use getTransactionsForAddress for backfills.
  • Batch reads with getMultipleAccounts in the keeper.

Frontend DAS calls are SWR-cached with a 60s TTL and in-flight dedupe.

Authority boundaries

ActorCan…Cannot…
UserDeposit, withdraw (USDC or in-kind), stake, claimMutate weights, pause, access keeper authority
KeeperPush NAV, submit rebalance_leg, post epoch Merkle rootMove user funds, bypass slippage or multiplier guards
Admin multisig (3/5 Squads v4)Pause, update weights, rotate keeper set, upgrade programsMint $VLT, skip the 48h timelock on param changes

See Security → Invariants for the complete on-chain invariant list.

On this page