Skip to main content
VaultAssertion is a standalone assertion runner. It observes Strategy Vault, protocol, and caller-supplied state and returns a structured report you can use to decide whether to build, send, or skip a transaction. It is complementary to VaultTransactionBuilder — caller-controlled, and entirely outside the transaction pipeline.
VaultAssertion does not build, mutate, or send transactions. It does not run a monitoring loop. It does not replace Squads policies or onchain validation hooks. It also does not require any transaction-builder input — you decide what to do with the report.

Quick Start

The two most actionable conditions for most strategy flows are ClmmPrice (live CLMM price/APY bounds) and KaminoLtv (Kamino obligation LTV bounds). The example below runs both in a single check and uses the result to gate the rest of the flow.
VaultAssertion.create accepts: assertion.check accepts a VaultAssertionCheckPlan: Invalid configuration throws VaultAssertionConfigError before any RPC reads or condition evaluation. Missing or undecodable on-chain state becomes a warn or block entry inside the returned report instead.

Report Shape

checks only contain user-supplied conditions. Slot drift failures are surfaced through causes and timing; they do not create synthetic checks.
Use causes for control flow (if (report.decision === "block") or for (const cause of report.causes)). Use checks for logging or audit display — every condition you supplied has a corresponding entry.

Timing

check() captures referenceSlot once at the start of the run. Each condition records the most recent slot observed through RPC, and the report tracks the maximum drift across the run. maxSlotDrift is optional and disabled by default. When supplied, drift is checked before and after each condition. If drift exceeds the threshold, the report blocks and the remaining conditions are skipped.
referenceSlot is a freshness and audit baseline. It is not an atomic multi-account snapshot guarantee. Transaction blockhash freshness and confirmation remain outside VaultAssertion — the caller’s transaction builder/sender owns those.

Thresholds

Built-in conditions accept inclusive thresholds in two flavors: Basis-point thresholds — used for APY-style metrics:
Raw numeric thresholds — used for prices, slot ages, and exchange rates:
NumericValue accepts number | string | bigint | BN | Decimal. At least one bound must be specified per threshold. Any of the following throws VaultAssertionConfigError before evaluation:
  • empty threshold objects
  • unknown threshold fields, or wrong-unit fields for a metric (e.g., warnAboveBps on a price threshold)
  • negative values, or non-integer BPS values
  • block/warn ordering that makes the block threshold less severe than the warn threshold
When a threshold is breached, the corresponding check evidence includes breachedThresholds for inspection.

Built-In Conditions

VaultCoreState

Asserts strategy vault owner and status-flag invariants. With no inputs, it just records vault state in the audit trail.

VaultPriceCoverage

Asserts that decoded ExponentPrices covers all required price IDs (or pairs) and is within a freshness bound.

KaminoLtv

Asserts a Kamino obligation’s LTV. The calculation is:
borrowingDisabled always blocks. A stale obligation warns when LTV and slot-age thresholds are otherwise passing.

AmmPrice

Asserts price evidence on a legacy AMM (Market Two) market. The market is loaded through Market.load(...).
At least one of impliedApy, ptPrice, or ptPriceInSy is required.
The loaded market’s vault.selfAddress is recorded as sourceVault evidence. It identifies the market’s underlying source — it is not a check that the strategy vault owns the market.

ClmmPrice

Asserts live CLMM price evidence. The evaluator fetches the market account, validates its program owner against EXPONENTCLMM_PROGRAM_ID, decodes it, then fetches the linked ticks account and performs the same validation.
At least one of impliedApy, ptPrice, or spotPrice is required.

OrderbookPrice

Asserts top-of-book APY and derived prices on an Exponent Orderbook. The evaluator filters expired offers, offers with non-positive amount or APY, and (by default) the assertion owner’s own non-virtual offers. Virtual offers are kept by default.
When both sides exist and source === "mid", the mid is used; with one side only, the available side is used.
Like AmmPrice, the loaded orderbook’s vault.selfAddress is recorded as sourceVault evidence. It is not a strategy-vault ownership check.

SyExchangeRate

Asserts a core vault’s SY/base exchange rate, loaded through CoreVault.load(...).

KaminoBorrowCapacity

Asserts that a Kamino obligation has the headroom to take on a new borrow at a target LTV. The evaluator loads the obligation, the collateral and borrow reserves, and computes the projected post-borrow LTV against maxLtvBps (with optional ltvBufferBps).

KaminoWithdrawCapacity

Asserts that a Kamino obligation has the headroom to withdraw collateral while still satisfying maxLtvBps. Symmetrical to KaminoBorrowCapacity on the withdraw side.

ClmmCapacity

Asserts that a CLMM swap of amountInAtomic from inputMint to outputMint is executable against the live tick state.

SyBacking

Asserts that an SY mint is sufficiently backed by its underlying. Useful pre-flight before stripping or redeeming SY-denominated positions.

PolicyCoverage

Asserts that the strategy vault’s policies cover a required list of programs. Useful for catching missing policies before a flow that depends on them.

WithdrawalQueue

Asserts the state of the strategy vault’s withdrawal queue — useful for managers running fill/execute flows who want to refuse to act when there’s an unfilled or past-due withdrawal.

Custom Conditions

Custom conditions let you express app-specific policy that doesn’t belong as a generic SDK condition. They share the same audit-trail and evidence semantics as built-in conditions.
The ctx (VaultAssertionContext) gives the evaluator first-class RPC access and report helpers: Helpers cache reads within a single check() run. Evidence is normalized into JSON-safe report data — BigInt, BN, Decimal, PublicKey, Date, Buffer, Uint8Array, non-finite numbers, and cyclic objects are all handled.
Invalid return values from evaluate(...) (e.g., a thrown error or a malformed result) are converted into blocking evaluation_error entries rather than crashing the whole run.

Gating a Transaction

The expected integration is caller-controlled: assert first, decide second, build/send last.
report.causes and report.checks are JSON-safe — log them, ship them to a dashboard, or surface them to a manager UI. The decision belongs to the caller.