Skip to main content

Vault mechanics

vault.clar is the protocol hub: it holds collateral, mints/burns shares, runs the weekly round lifecycle, and settles against the oracle. All sBTC amounts are in sats (1e8 = 1 sBTC); USDC in micro-USDC (1e6 = 1 USDC); prices / IV / rates in 1e8 fixed point.

Depositing

(deposit (amount uint) (sbtc <sip010>))

A depositor sends sBTC and receives bcSHARE pro-rata (1:1 on the first deposit). The vault:

  1. checks it is not paused and that the token argument is the configured sBTC,
  2. requires amount ≥ min-deposit (default u100000 = 0.001 sBTC),
  3. computes shares = amount · supply / total-collateral (or = amount if supply is 0),
  4. harvests any accrued premium for the user (premium-per-share index),
  5. pulls the sBTC (contract-call? sbtc transfer …) into the vault,
  6. mints bcSHARE via bcshare-token.mint,
  7. re-checkpoints the user's premium index and emits a deposit print event.

The web app attaches a fungible-token post-condition (willSendEq(amount)) so the wallet guarantees exactly amount sBTC leaves the user.

Withdrawing

(withdraw (shares uint) (sbtc <sip010>))
(request-withdraw (shares uint))
(cancel-withdraw-request (shares uint))

Burning shares returns shares · collateral / supply sBTC — but only the idle (not call-backed) portion can leave. Collateral locked behind written contracts in the active round cannot be withdrawn (ERR-LOCKED u115). request-withdraw queues shares so the matching collateral is excluded from new call-writing and frees up at the next settlement.

Premium accounting

Premium accrues to depositors through a cumulative premium-per-share index (acc-premium-per-share, scaled by 1e12). On every balance-changing action the vault harvests (moves newly accrued premium into the user's pending bucket) and checkpoints (re-anchors their debt to the current balance). Premium is claimed in USDC:

(claim-premium (usdc <sip010>))

Round lifecycle

(start-round (otm-bps uint) (iv uint)) ;; keeper only
(buy-call (contracts uint) (usdc <sip010>)) ;; permissionless
(settle-round) ;; keeper from expiry; anyone after the grace window
(exercise (round-id uint) (sbtc <sip010>)) ;; buyer pulls ITM payout
  1. start-round — the keeper opens a round at strike = spot · (1 + otm-bps/10000), reading spot from the oracle. Bounds: otm-bps ∈ [otm-min, otm-max], iv ∈ [iv-min, iv-max]. The previous round must be settled.
  2. buy-call — anyone buys contracts 1-sBTC calls. The premium is priced live by bs-math.bs-call-price from current spot, the round's strike/IV, the remaining tenor, and the risk-free rate, then converted to micro-USDC. The buyer's USDC is pulled in; premium accrues to depositors immediately. You can never buy more than contracts-available (whole idle sBTC, minus queued-withdrawal collateral).
  3. settle-round — after expiry, settles against the oracle. The settlement price must postdate expiry (ERR-STALE-SETTLEMENT u122). Computes payout-per-contract = (S_exp − K)/S_exp (0 if S_exp ≤ K), reserves the total payout out of collateral.
  4. exercise — each ITM buyer pulls their payout-per-contract · contracts sBTC. Always less than 1 sBTC per contract, so the pool stays solvent.

Always fully collateralized

Because a contract's maximum payout is (S_exp − K)/S_exp < 1 sBTC and the pool writes at most one contract per whole idle sBTC it holds, settlement is always covered by collateral already in the vault. No external liquidity, no liquidation engine, premium only flows in.

Admin & RBAC

set-keeper, set-owner, pause / unpause, set-config, and set-tokens are owner-gated (keeper-gated for start-round). pause blocks deposits / buys / round-starts, but leaves withdrawals, claims, settlement and exercise open — pausing can never trap user funds. set-tokens only works before any shares exist.

Read-only views

get-vault-state, get-round, get-position, get-user, preview-deposit, preview-withdraw, preview-quote, preview-quote-current, contracts-available, get-config. The web app's chain-direct mode reads these directly.

Error codes

u100 paused · u101 min deposit · u102 round active · u103 round not active · u104 expired · u105 not expired · u106 insufficient coverage · u107 not keeper · u108 not owner · u109 stale price · u110 bad price · u111 transfer failed · u112 wrong token · u113 zero amount · u114 insufficient shares · u115 locked by round · u116 no position · u117 already claimed · u118 not settled · u119 bad params · u120 nothing to claim · u121 settle in grace (keeper-only window) · u122 settlement price predates expiry · u401 bcSHARE not authorized · u1001 BS domain.