Architecture
Levant is a single EIP-2535 Diamond contract living at one address on Robinhood Chain. Every user action routes through it to a small set of stateless facets that share one append-only storage layout — so the protocol can be upgraded function by function without ever moving your positions, LP shares, or vault balances.
One address, many facets
Levant is built as an EIP-2535 Diamond. There is a single on-chain contract — the Diamond, at 0xE07098bc29Ebe50E6203606bc2eA0e1E189cb7e2 on Robinhood Chain (chainId 4663) — and it is the only address you ever interact with. Opening a trade, adding margin, verifying a price, liquidating an underwater position, transferring ownership: every call goes to that one contract. Behind it, the actual logic is split across separate facet contracts, each responsible for one job. The Diamond owns all the state; the facets are stateless code it borrows at call time.
When a call arrives, the Diamond's fallback looks up the function's 4-byte selector in its routing table, finds the facet that implements it, and delegatecalls into that facet. Because it is a delegatecall, the facet's code executes inside the Diamond's own storage — it reads and writes the Diamond's state, not its own. This is what lets many separate contracts behave as one coherent protocol at a single address, and it is what makes upgrades possible without redeploying or migrating anything.
call → Diamond (0xdDfcEEF4…8097)
fallback() reads ds.selectorToFacetAndPosition[msg.sig].facetAddress
require(facet != address(0)) // "Diamond: Function does not exist"
delegatecall(facet, calldata) // runs in the Diamond's storage
return / revert (bubbled up verbatim)The facets and what they do
Five facets are wired into the Diamond today. Two are the standard Diamond machinery (cut and loupe); three carry Levant's protocol logic (trading, oracle, ownership). Each facet is deployed at its own address, but you never call those addresses — they are listed here only so the wiring is auditable on-chain.
| Facet | Address | Responsibility |
|---|---|---|
| DiamondCutFacet | 0xC330b9C4Ad5Af1329d8b2652A8376780900fc09A | Upgrades. Exposes the single diamondCut entrypoint that adds, replaces, or removes facet functions. Owner-only. |
| DiamondLoupeFacet | 0xa2D8745fCD86Df2A2e40D24d54c379D9F87e0E8c | Introspection. facets, facetAddresses, facetFunctionSelectors, facetAddress, plus ERC-165 supportsInterface — anyone can enumerate exactly what code the Diamond runs. |
| TradingFacet | 0xf56dad949472A67b222462C6B0Db128DB4Feb06b | The trading engine. Intents (submitOpenIntent, submitLimitIntent), keeper fills (executeOpen, executeClose, executeCloseTrigger), exits (closePosition, closePartial), margin (addMargin, removeMargin), setTpSl, cancelIntent, and permissionless liquidatePosition. |
| SignedPriceOracleFacet | 0x7BCa681C11ac4dc822095457662dCcD3004349F5 | Price verification. getVerifiedPrice validates an EIP-712 m-of-n signed report before any fill or liquidation; the owner manages the signer set, threshold, and max price age. |
| OwnershipFacet | 0xe50A7E73e73Cc96b976CfBfA8b93770688019Af0 | ERC-173 two-step ownership. owner, pendingOwner, transferOwnership, acceptOwnership, cancelOwnershipTransfer. Deliberately no renounce. |
The LevantVault is a separate contract, not a facet. It is an ERC-4626 vault at 0xC90D98cFeE95F481aDE30d95AD88f01B6C65ad5C that issues lvUSDG shares against USDG collateral (a 6-decimal stable at 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168). LPs deposit into the vault directly; it is the counterparty to every trade, collecting fees and trader losses and paying trader profits. The Diamond holds a pointer to the vault in its trading storage and drives it — locking reserved liquidity, paying profits, collecting losses and fees — but the vault's assets and LP shares live in the vault contract itself, entirely outside the Diamond's upgradeable surface.
Escrow, the keeper, and on-chain guards
Trades settle in two steps, and that split is a deliberate trust boundary. Step one: the trader calls submitOpenIntent (market) or submitLimitIntent (limit/stop) on the Diamond, which pulls their collateral into escrow — the Diamond holds it, never the keeper. Step two: an off-chain keeper watches for the intent and calls executeOpen with an EIP-712 signed price report. Closing is symmetric: the trader flags closePosition/closePartial, then the keeper settles with executeClose. The keeper runs on a server, is non-custodial, and can only ever act within the on-chain guards below — it cannot invent a price, move user funds, or fill outside the trader's stated terms.
- Mandatory slippage tolerance. Every open and limit intent carries a
maxSlippageBpsin the range[1, 500]— there is no implicit default. If the entry spread pushes the fill further from the oracle mid than that tolerance,executeOpenrevertsSlippageExceeded; the intent stays pending (not consumed) and the escrow is fully reclaimable withcancelIntent. - Liquidatable-at-open guard.
executeOpenrefuses to mint a position that would already be at or under its maintenance margin at the mark it filled against (revertsLiquidatableAtOpen), and the whole transaction rolls back so no open fee is taken. At very high leverage the entry spread alone can trip this. - Verified price only. Every fill, close, and liquidation prices against a signed report the keeper cannot forge, checked by the SignedPriceOracleFacet inside the same delegatecall. Liquidation is permissionless — anyone can submit a signed report to close an underwater position — precisely because the price is authenticated.
Shared, namespaced storage
All facets read and write the same underlying storage, but they do not fight over slots. Levant uses the Diamond-Storage pattern: each domain declares one struct and pins it to a fixed slot derived from a unique string hash, far apart from Solidity's normal sequential layout. A facet fetches its struct with a tiny assembly helper that sets the struct's slot to that constant, so unrelated domains can never collide no matter what order facets are added.
// Diamond core (routing table + owner) — LibDiamond
bytes32 DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage");
// Trading engine (positions, intents, markets, funding) — LibTrading
bytes32 STORAGE_POSITION = keccak256("levant.trading.storage");
// Ownership and the signed-oracle each occupy their own namespaced slot too:
// keccak256("levant.ownership.storage")
// keccak256("levant.signedoracle.storage")The Diamond-core struct holds the wiring itself: selectorToFacetAndPosition (which facet serves each function), facetFunctionSelectors, the facetAddresses array, ERC-165 supportedInterfaces, and contractOwner. The trading struct holds the protocol's economic state: the vault and collateral addresses, the keeper, ID counters, reservedLiquidity, and the maps of positions, pendingOpens, pendingClose, per-market markets config, and marketState (open interest and funding accumulators). Values are 6-decimal for collateral/margin/notional/fees, 1e18 for prices, and integer leverage.
Storage is append-only
A Diamond keeps its storage across upgrades, so the layout of each struct is treated as permanent. New state is only ever appended to the end of an existing struct, or placed in a brand-new namespaced struct — fields are never reordered, inserted, or retyped. Levant's own history shows the discipline: the Position struct began minimal and grew field-by-field as features shipped (openedAt, the funding accumulator snapshot, take-profit/stop-loss, and finally an explicit notional), and the trading struct's pendingCloseBps map was appended last for partial closes — always at the end, never in the middle.
Upgrades via diamondCut
The protocol is upgraded with a single owner-only function, diamondCut. A cut is a list of operations, each an Add, Replace, or Remove of specific function selectors pointing at a facet address, optionally followed by an initializer that runs once via delegatecall. Add wires new selectors to a facet, Replace repoints existing selectors to new code, and Remove unhooks them. That is the entire upgrade surface — it edits the routing table, nothing more.
diamondCut(
[ { facetAddress, action: Add | Replace | Remove, functionSelectors[] } ],
init, // optional one-time initializer (address(0) = none)
calldata
)
// rewires selector → facet code only; the storage structs are never touchedBecause a cut only changes which code answers which selector — and never the storage those functions read — upgrading a facet leaves every piece of live state exactly where it was. The following all survive a diamondCut untouched:
- Open positions — every trader's collateral, entry price, leverage, funding basis, reserved liquidity, and TP/SL remain byte-for-byte in the trading struct.
- Vault and LP shares — the LevantVault is a separate contract; the Diamond only stores a pointer to it, so
lvUSDGshares and vault assets are wholly outside the cut. - Reserved / locked liquidity —
reservedLiquidityand the vault's locked-asset mirror are storage values, unaffected by swapping facet code. - Market config and state — per-market risk parameters, open interest, and funding accumulators persist across the upgrade.
- Ownership and the signer set — the owner, pending owner, price signers, threshold, and max price age all live in their own namespaced storage.
Ownership: ERC-173, two-step
The owner is the most powerful account in the system — it can diamondCut any facet, add or remove price signers, and rewrite market risk parameters. To make that power hard to lose by accident, ownership follows ERC-173 with a two-step handover. The current owner calls transferOwnership to nominate a pendingOwner; nothing changes until the nominee itself calls acceptOwnership, which proves the new address is real and controllable. The owner can call cancelOwnershipTransfer to withdraw a pending nomination before it is accepted.
renounceOwnership. An ownerless Diamond could never be cut again, so a bug in any facet would be permanent — Levant keeps the owner slot always occupied so the protocol stays upgradeable.Oracle binding to the Diamond
Price verification is itself a facet, and its EIP-712 domain is bound to this exact deployment. The domain separator commits to the chainId and the verifyingContract — and because the oracle code runs under delegatecall, verifyingContract resolves to the Diamond's address, not the facet's. Each report also binds the marketId, the 1e18 price, and a publishTime. So a signed report only validates on this chain, against this Diamond, for the market the caller asked for, inside a live time window: it is rejected if replayed to another chain, another contract, or a different market; if it is older than the 60-second max price age; or if it is dated more than a few seconds in the future. Recovered signers must be whitelisted and strictly ascending, so one key can never satisfy the threshold twice. The oracle is m-of-n by design and 1-of-1 today, with the keeper as the sole signer; the keeper sources prices from a multi-source off-chain feed and signs them.
Build, language, and tests
| Aspect | Detail |
|---|---|
| Compiler | Solidity solc 0.8.28 |
| Toolchain | Foundry (forge build / forge test) |
| Test suite | 171 passing tests |
| Standards | EIP-2535 (Diamond), ERC-173 (ownership), ERC-165 (interface detection), ERC-4626 (vault), EIP-712 (signed prices) |
| Units | 6-decimal collateral / margin / notional / fees; 1e18 prices; integer leverage |
Deployment reference
| Contract | Address |
|---|---|
| Diamond (proxy — all user actions) | 0xE07098bc29Ebe50E6203606bc2eA0e1E189cb7e2 |
| LevantVault (ERC-4626, lvUSDG shares) | 0xC90D98cFeE95F481aDE30d95AD88f01B6C65ad5C |
| USDG collateral (6-dec, open mint on testnet) | 0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168 |
The Diamond is deployed on Robinhood Chain mainnet (chainId 4663, gas paid in Sepolia ETH) at block 88997025, and lists 20 crypto perpetual markets, all sharing the same config today: 100x max leverage, 0.08% open and close fees, 900% max profit, a 0.02% base spread, 10% maintenance margin, and a 5% liquidation fee. The five facet addresses above are all reachable through the DiamondLoupeFacet's facets view for independent verification.
