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.
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
The effective loan-to-value for an asset at time t is:
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:
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:
- 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)
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)
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.
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.
baseRate + slope1 + slope2 × (U − U*) / (1 − U*) if U > U*
Defaults: U* = 80%, base = 0, slope1 = 4%, slope2 = 60%. Reserve factor per asset (10–20%) is captured as protocol income.
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.
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
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
Starting risk grid for the live deployment. All values are governance-adjustable behind a 48-hour timelock.
| Asset | baseLTV | baseLT | σ baseline | k_vol | minVolAdj | earningsFloor | reserve |
|---|---|---|---|---|---|---|---|
| rSPY | 80% | 85% | 15% | 1 | 75% | 90% | 10% |
| rAAPL | 70% | 75% | 25% | 1.5 | 55% | 60% | 15% |
| rNVDA | 60% | 68% | 45% | 1.5 | 55% | 50% | 20% |
| rTSLA | 55% | 65% | 55% | 2 | 45% | 45% | 20% |
Contracts
All contracts on Robinhood Chain mainnet · chain 4663.
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
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.