FxVault that converts the chain’s dollar into the synthetic at the oracle rate and back. Traders normally reach it through the Router; this page is for integrations that mint or redeem directly, or that underwrite a vault. Examples use the wINR vault on Robinhood Chain (find every vault in Addresses).
Read the vault
status() gives everything a UI needs: the rate the vault will use (INR per USD, 1e18), whether it is fresh (normal fees, underwriting open) or stale (1% fee, underwriting closed), raw-dollar assets, liability and signed equity, and the collateral ratio in bps (2^256 - 1 when nothing is minted). It reverts StalePrice(code, age) once the rate is older than 5 days; handle that as “oracle offline” and fall back to assets() plus the last known rate if you need numbers.
Mint
1
Preview
2
Approve and mint
mint reverts Slippage below minSynthOut, LiabilityCapExceeded if the vault’s total liability would pass 1,000,000 dollars, CollateralRatioTooLow if assets would fall below 100% of liability (cannot happen from a mint at 100% unless the vault is already under water), and EnforcedPause while paused. The synthetic lands in the caller’s wallet and can be sent, traded on any curve in that currency, or redeemed.
Redeem
No approval: the vault burns the caller’s synthetic.previewRedeem already includes the pro-rata haircut when assets < liability; the Redeemed event reports haircut = true in that case. Redeeming is never paused and never blocked by the collateral ratio. In the stale window both directions pay staleFeeBps (1%) instead of 0.3%.
Underwrite (deposit and withdraw)
Underwriters supply equity, receive the vault’s share token (uw-wINR, 18 decimals) and earn 80% of every mint and redeem fee. Both actions need a fresh price.
NotFresh outside the fresh window, NoEquity when equity is zero or negative (deposits after the first, and every withdrawal), CollateralRatioTooLow when a withdrawal would leave assets below minCRBps of liability, InsufficientAssets when the payout exceeds assets(). donate(usdcIn) adds equity without shares and needs only an approval.
An underwriter’s position value is shares * equity / totalSupply in raw dollars; it rises with fees and falls when the currency strengthens against the dollar (liability grows). Historical vaultSnapshot rows from the indexer give equity over time for an APY estimate (see Vaults query).
Price updates: priceUpdate and value
Every state-changing vault function is payable and takes bytes[] priceUpdate. The vault forwards oracle.updateFee(priceUpdate) to oracle.update and refunds the rest of msg.value. With the deployed KeeperFxOracle the fee is always 0: pass [] and value: 0n, as above.
If a vault were created against a PythFxOracle, the flow would be:
Handling StalePrice
rate() and every function that calls it revert with StalePrice(bytes32 code, uint256 age) once the price is older than maxAgeStale (5 days). Decode it with viem’s decodeErrorResult against fxVaultAbi (the error is declared on the oracle; keeperFxOracleAbi has it) or simply treat any revert from a preview as “vault unavailable”. Curve trading in the quote asset keeps working through a stale oracle; only conversions to and from dollars stop. The keeper heartbeats hourly, so this state means the keeper has been down for days.
Reading vault history
The indexer stores avaultEvent (mint, redeem, deposit, withdraw, donate) and a vaultSnapshot (assets, liability, equity, crBps, synth and share supply at that block) for every event, plus rate rows from RatePosted. See Vaults query and Rates query.