RiskEngine is a stateless contract — it stores no user positions. On every call it reads position data from the caller’s SmartAccount and LendingPools, prices everything via Oracle, and returns a boolean. It never modifies state in those contracts; it only reads.
Testnet address: CBL7RCG5H4VIZCNF7BRM2FQFXK7N5KRQKW7ZVEQZJKNXHA6FEU4OXK5I
Guard Functions
These are the three functions that gate every state-changing operation in the protocol. AccountManager calls the appropriate guard before every borrow, withdrawal, or liquidation.is_borrow_allowed
true if borrowing borrow_amt of symbol from the pool would leave the account healthy. Uses the gross-asset model — new borrows count as both an asset (purchased) and a liability simultaneously.
Gross-asset borrow check:
is_withdraw_allowed
true if withdrawing withdraw_amt of symbol collateral would leave the account healthy.
Withdraw check:
is_account_healthy
true if:
BALANCE_TO_BORROW_THRESHOLD = 1.1 × 10¹⁸.
Used internally by is_borrow_allowed, is_withdraw_allowed, and by liquidation callers to check if an account is eligible for liquidation.
Portfolio Query Functions
get_current_total_balance
margin_account, in USD WAD. Iterates every token in SmartAccount.get_all_collateral_tokens() and prices each one.
Collateral valuation by type:
| Token type | Valuation method |
|---|---|
| XLM, USDC | balance_wad × oracle_price_wad / WAD |
| BLEND_XLM, BLEND_USDC | b_tokens × b_rate / 10¹² → scale to WAD → × oracle price |
| AQ_XLM_USDC, SS_XLM_USDC (LP tokens) | $0 — tracked but not counted as collateral |
get_current_total_borrows
margin_account, in USD WAD. Queries each LendingPool’s get_borrow_balance() for each symbol in BorrowedTokensList.
Blend b-Token Valuation
When SmartAccount holds Blend b-tokens (received by depositing into Blend pool), the RiskEngine values them using the Blend pool’sb_rate:
b_rate is read directly from Blend’s pool reserve data. It represents the exchange rate from b-tokens to the underlying asset.
Symbol Canonicalization
Before querying Oracle prices, the RiskEngine maps wrapped token symbols to their underlying asset for pricing:| Input symbol | Canonical → Oracle feed |
|---|---|
BLUSDC | → USDC |
AQUSDC | → USDC |
SOUSDC | → USDC |
AQUARIUS_USDC | → USDC |
SOROSWAP_USDC | → USDC |
BLEND_XLM | → XLM |
BLEND_USDC | → USDC |
AQ_XLM_USDC, SS_XLM_USDC, AQ_XLM_AQUA, AQ_XLM_USDT) have no oracle feed and are valued at $0.
Price Caching
Within a singleis_borrow_allowed or get_current_total_balance call, oracle prices are fetched once per symbol and cached in a local Map<Symbol, u128>. This avoids duplicate cross-contract oracle calls when the same symbol appears multiple times (e.g., both as collateral and debt).
Dust Threshold
Debts below10^16 WAD (0.01 token in 18-decimal terms) are ignored during health checks. This prevents rounding-dust positions from blocking otherwise-healthy accounts.
Constants
| Constant | Value | Description |
|---|---|---|
BALANCE_TO_BORROW_THRESHOLD | 1.1 × 10¹⁸ | Minimum health factor |
dust_debt_threshold | 10^16 | Minimum meaningful debt |
SCALAR_12 | 10¹² | Blend b_rate divisor |
Error Codes
Cross-Contract Reads
The RiskEngine reads from, but never writes to, other contracts:| Contract | What it reads |
|---|---|
SmartAccount | get_all_collateral_tokens(), get_collateral_token_balance(symbol), get_all_borrowed_tokens() |
LendingPool XLM/USDC | get_borrow_balance(margin_account) |
TrackingToken | balance(margin_account, symbol) for Blend/LP position sizes |
Oracle | get_price_latest(symbol) |
Blend Pool | Reserve data including b_rate |
Registry | Contract addresses |
Related
- Math Reference — complete health factor formula with worked examples
- AccountManager — the only contract that calls RiskEngine guards
- Oracle — price feed source
- Health Factor — user-facing explanation of the 1.1× invariant

