§ Overview

Overview

Marginal is a lending market for tokenized US equities on Robinhood Chain. It is architecturally an Aave v3-style overcollateralized money market: users supply collateral tokens and borrow stablecoins at a variable rate. What differs is the risk engine.

The core innovation is that every position's loan-to-value is a live function of realized volatility and the earnings calendar. There is no "governance-set LTV per asset". Instead, each asset carries a baseLtv ceiling, and the RiskEngine multiplies that ceiling by three time-varying factors before every solvency check.

The result: leverage compresses instantly when volatility spikes; it ramps down deterministically in the 7 days before every scheduled earnings print; and it stays fully available in calm markets.

Markets
6
Chain
4663
Solvency check
per tx
Tests passing
23/23
§ Architecture

Architecture

The protocol has eight on-chain components. Only Pool and LiquidationEngine can move user funds. Every other contract is either a config surface or a pure risk computation.

user ─┬─► Pool.supply/borrow/repay/withdraw
      │       │
      │       ├─► RiskEngine.getEffectiveRisk(asset)
      │       │       │
      │       │       ├─► AssetRegistry.getConfig(asset)          [baseLTV, baseLT, cfg]
      │       │       ├─► StockOracleAggregator.getPricePoint()   [priceE8, realizedVolBps]
      │       │       └─► EarningsCalendar.nextEarnings(asset)    [t_e]
      │       │
      │       ├─► AToken.mint / burn / transferUnderlyingTo
      │       └─► VariableDebtToken.mint / burn
      │
      └─► LiquidationEngine.computeLiquidation(user)   [called via Pool.liquidationCall]

admin ──► AssetRegistry.listAsset / updateConfig / setPaused
          KYCRegistry.setAllowed / setOpenRegistration
          StockOracleAggregator.pushUpdate (per-block price + vol)
          EarningsCalendar.schedule
§ Dynamic LTV formula

Dynamic LTV formula

The effective loan-to-value for an asset at time t is:

LTV_eff(a, t) = LTV_base(a) × f(σt) × g(te) × h(TVL)

Each factor is bounded to [0, 1] and multiplied. The liquidation threshold LT_eff compresses at half the rate of LTV so healthy positions get a runway to rebalance rather than being force-liquidated into a spike:

LT_eff = max(LT_base − ½ · (LTV_base − LTV_eff), LTV_eff)
§ Volatility term · f(σ)

Volatility term · f(σ)

Realized 30-day volatility is pushed to the oracle hourly during regular trading hours by a signed off-chain feed. When σ exceeds the per-asset baseline, the factor is haircut linearly:

f(σ) = clamp(1 − kvol × max(0, σ − σbaseline), minVolAdj, 1)
  • k_vol — sensitivity, default 1.5× for large caps, 2.0× for <$50B cap.
  • σ_baseline — the asset's trailing 2-year median realized vol.
  • minVolAdj — per-asset floor (default 0.55). Prevents adversarial vol prints from collapsing LTV to zero.

Worked example (rNVDA): σ_baseline = 45%, current realized σ = 68%. f(σ) = 1 − 1.5 × (0.68 − 0.45) = 1 − 0.345 = 0.655. At baseLtv = 60%, effective LTV = 60% × 0.655 = 39.3%.

§ Earnings term · g(t_e)

Earnings term · g(t_e)

The earnings calendar oracle exposes the next scheduled earnings timestamp per asset. Let Δ = te − t (seconds until earnings). The factor is a piecewise ramp:

g(Δ) =
    1.00                   Δ > 7 days                    (calm window)
    linear 1.00 → 0.70     1d < Δ ≤ 7 days               (walk-down)
    0.60                   0 < Δ ≤ 1 day                 (day-of)
    earningsFloor          −1 day ≤ Δ ≤ 0                (overnight)
    linear floor → 1.00    −2 days ≤ Δ < −1 day          (recovery)
    1.00                   Δ < −2 days                   (post-earnings)

earningsFloor is per-asset. Ranges from 0.90 for rSPY (broad ETF, small avg print move) to 0.45 for rTSLA.

§ Concentration term · h(TVL)

Concentration term · h(TVL)

Guards against a whale depositing an unusually large fraction of an asset's on-chain float, then having their position size distort the pool's collateral base. Below a governance-set concentrationCap, no adjustment. Above it, LTV falls linearly to zero.

h(TVL) = min(1, on_chain_TVL(a) / concentrationCap(a))
§ Interest rate model

Interest rate model

Two-slope kink model on utilization. Below the optimal utilization the borrow rate rises with slope1; above it, slope2 (steep) kicks in to discourage full utilization.

borrowRate(U) = baseRate + slope1 × U / U*  if U ≤ U*
baseRate + slope1 + slope2 × (U − U*) / (1 − U*)  if U > U*
supplyRate = borrowRate × U × (1 − reserveFactor)

Defaults: U* = 80%, base = 0, slope1 = 4%, slope2 = 60%. Reserve factor per asset (10–20%) is captured as protocol income.

§ Liquidations

Liquidations

A position with health factor < 1.0 becomes liquidatable. Liquidators call Pool.liquidationCall supplying the debt asset; they receive collateral at price × (1 + bonus). The bonus itself is dynamic — it scales with realized volatility so liquidators are compensated for closing violent-name positions.

bonus(a, t) = clamp(baseBonus + kbonus × max(0, σ − σ_baseline), 5%, 12%)

Close factor is 50% — a single liquidation can retire at most half of a user's debt. Prevents whale-liquidations from clearing a position that could self-heal via a small rebalance.

§ Oracles

Oracles

Three feeds power the risk engine.

  • Price feed — push-based, admin-signed. Widened staleness window (7 days on the live deployment). Cross-checked against a secondary Chainlink CCIP-relayed price when enabled.
  • Realized-vol feed — 30-day annualized Yang-Zhang estimator computed off-chain, pushed hourly during RTH.
  • Earnings calendar — per-ticker next-earnings timestamp, with a 24-hour delay applied to updates that move the earnings date closer to the present (prevents griefing that would freeze LTV).
§ Risk parameters

Risk parameters

Starting risk grid for the live deployment. All values are governance-adjustable behind a 48-hour timelock.

AssetbaseLTVbaseLTσ baselinek_volminVolAdjearningsFloorreserve
rSPY80%85%15%175%90%10%
rAAPL70%75%25%1.555%60%15%
rNVDA60%68%45%1.555%50%20%
rTSLA55%65%55%245%45%20%
§ Contracts

Contracts

All contracts on Robinhood Chain mainnet · chain 4663.

Pool
0x7630d97702c187748e53d5e3ac6d3a67d95c53f2
Explorer ↗
RiskEngine
0xc5730b97f7122a1e3280aa085d8aa991b94ca082
Explorer ↗
LiquidationEngine
0xc53aabb1cba26dd845eedf74c7898355aa353a7e
Explorer ↗
AssetRegistry
0x89de9ea1149c74999706613f9a665d89622cd6f9
Explorer ↗
KYCRegistry
0xbef59fb8cfe7c20cf6e7e5124491365f7081b65a
Explorer ↗
StockOracleAggregator
0xd6cb81586898b734410f0a56099b8f3b28b175aa
Explorer ↗
EarningsCalendar
0xd6466d1cbe1e7aaf13b3a92bbc1b42dc175789b0
Explorer ↗
Faucet
0xfccabdc83ab305e0667f466ecfe27e6df3d72399
Explorer ↗
§ Security model

Security model

The protocol has four security layers.

  • Solvency — every user-facing entry point on Pool calls RiskEngine.getUserAccountData at the end; any action that would result in HF < 1 reverts.
  • Access control — collateral tokens are MarginalTokens where mint() requires MINTER_ROLE. Only the Faucet holds that role, and the Faucet has a 24h per-user-per-asset cooldown.
  • Oracle guards — signed updates, deviation guard on secondary feed, admin-configurable staleness window, and a 24h delay on any earnings update that moves the date closer to now.
  • Compliance gate — Pool wraps every user entry in onlyKYCd. Users self-register once via KYCRegistry.selfRegister() while open-registration mode is on; admin can flip it off any time.

Report a security issue: security@marginal.finance.

§ FAQ

FAQ

Is Marginal audited?+

No. The contracts are open source and have a full Foundry test suite (23/23 passing) but have not been through a formal third-party audit. Treat every position on the live Robinhood Chain deployment as testnet.

Are the stock tokens on Marginal actually redeemable for shares?+

No. The listed tokens (rSPY, rAAPL, rNVDA, rTSLA) are demonstration ERC-20s minted from an on-chain Faucet with per-address rate limits. They are not real Robinhood Securities-issued equity tokens.

Why does MetaMask show a warning?+

Blockaid (MetaMask's real-time phishing filter) flags brand-new .finance domains that connect wallets. It is a false positive. See /security for the disclosure policy and the submitted appeal.

Why per-asset per-24h rate-limited Faucet?+

So an attacker can't loop the mint function to inflate collateral and drain the pool. This is what makes the live deploy non-drainable even though the underlying tokens are testnet-mintable.

How is the health factor different from Aave's?+

It uses the same 1e18 scale and the same denominator (weighted LT × collateral). What differs is the *inputs*: our liquidation threshold is a live function of realized volatility, earnings distance, and asset concentration — not a static parameter.

Can I integrate as a lender or bot?+

Yes. The pool exposes standard Aave-shaped external functions. See Contracts below for the ABI locations, and RiskEngine.getUserAccountData() for the read call that returns solvency.

Something missing? Email hello@marginal.finance or report a security issue to security@marginal.finance.