Oracle Architecture
The reference-price path for QEURO valuation: a dual-source OracleRouter whose active EUR/USD source
is the Hyperliquid xyz:EUR perpetual mid (the venue where the protocol hedge executes), with the
ChainlinkOracle retained as a one-transaction fallback. Live on Base mainnet since 2026-06-25.
Why hedge-aligned pricing
The protocol neutralizes the EUR/USD leg with a hedge on Hyperliquid. Pricing QEURO mint/redeem off a generic spot feed while the hedge fills at the venue price leaves a persistent basis between the QEURO liability and its hedge. Reading the venue mid aligns valuation more closely with the hedge, but does not eliminate basis, spreads, funding, timing or execution risk. Normal user amounts additionally come from directional depth and buffers in ExecutionPricing. Chainlink spot is kept as a safety reference and fallback, not as the primary valuation source.
Components
| Contract | Source | Role |
|---|---|---|
OracleRouter | src/oracle/OracleRouter.sol | Routes IOracle reads to the active oracle; switchOracle |
HyperliquidEurUsdOracle | src/oracle/HyperliquidEurUsdOracle.sol | Active EUR/USD source (router slot 1) |
ChainlinkOracle | src/oracle/ChainlinkOracle.sol | Fallback EUR/USD (slot 0) + USDC/USD validation |
StorkOracle | src/oracle/StorkOracle.sol | Legacy/parked (slot 1 now holds the Hyperliquid oracle) |
LighterEurUsdOracle | src/oracle/LighterEurUsdOracle.sol | Deployed 2026-07-17 (0xcd53182a430d48Be0f414CCA022ABA5d05903536), inert: no router slot; the Lighter venue was not adopted (2026-09-01) |
SlippageStorage | src/oracle/SlippageStorage.sol | On-chain store the off-chain publisher writes the mid into |
IOracle / IHyperliquidOracle | src/interfaces/ | Oracle-agnostic interface + adapter extensions |
Live addresses (Base, chain 8453)
OracleRouter 0x7ED6aaEd83Db69509A88CAe5C247ef8fA44056E0
HyperliquidEurUsdOracle 0x0B58aBB57775E0fCEDfd4460e00dD9D9610C2C43
ChainlinkOracle 0xaEE3c9c298051ef7242882AbCaE2Fd12d29443E7
SlippageStorage 0x0fde0ff2566be3c24af6d654012dddb4f1da099b
TimeProvider 0x520236487CBD0a6958B4EefC7853cd7C3F5C56E7
QuantillonVault 0x833E5Ba510a241b21F1C60c987D1c49eB52E4a07
Safe (Multisig) 0x1d7fF432a93d0085Fb69474c7E567f859829e6cd
All contracts are verified on Basescan. Proxy addresses are the stable reference; deployment records are tracked in deployments/8453/versions.json, but can lag subsequent upgrades. Read live version() and active module bindings; see Production Protocol Reference.
The active publication route uses ReportPublicationBatcher v1.0.0 at
0xBdd672FB97ecC9f5c8eBcD783a2Fe1234cA1a5FB, bound to SlippageStorage and
ExecutionPricing v1.3.2 at 0xFA894CD2e0C8030c95925FfF3b8206F397e0D897.
Its immutable writer is the existing publisher account. The batcher itself needs
SlippageStorage WRITER_ROLE plus ExecutionPricing WRITER_ROLE and REPORTER_ROLE.
Price, depth and reconciled capacity are independently accepted; check each target's
events and timestamps. Depth/capacity have a 60-second maximum age, separate from
the market oracle's price-staleness limit. See Deployment.
Independent reference configuration
Hyperliquid remains the live EUR/USD pricing source. setReferenceCheck adds an
independent Chainlink divergence bound; it does not switch execution pricing to
Chainlink and does not impose a calendar-based weekend shutdown. The off-hours
setting selects a divergence limit, not an open/closed flag. The reference must
still pass round, sequencer, timestamp and absolute-price checks.
The strict reference check has been active since 23 September 2026. A stale, unavailable, invalid or excessively divergent Chainlink EUR/USD reference makes the market oracle invalid and blocks minting and normal redemption, including on weekends. This availability tradeoff is intentional. Initial release settings are 200 bps normal divergence, 300 bps off-hours divergence and 8,100 seconds maximum reference age; the independent probe's own checks remain authoritative. These settings were verified after activation; read live getters for subsequent governance changes.
Configure the reference only after the Chainlink implementation exposes
peekEurUsdPrice(). Read maxReferenceDivergenceBps,
maxReferenceDivergenceOffHoursBps, and maxReferenceAge on chain: zero normal
divergence disables the check. Chainlink's independent probe also enforces its
own freshness ceiling, so increasing the market adapter's age limit cannot
bypass it. Validate the actual configured feed across weekday and weekend
observations before activation. Historical continuity does not guarantee future
feed availability; invalid reference data must not silently disable the bound.
Likewise, SlippageStorage's setMidDriftGuard requires both a nonzero basis-point
limit and a nonzero window to activate cumulative drift protection. Test accepted
price events through the real report batcher after changing either configuration.
Data flow
Hyperliquid info API (allMids xyz:EUR)
│ off-chain Slippage Monitor (separate backend repo)
▼
SlippageStorage.getSlippageBySource(SOURCE_HYPERLIQUID=1) → { midPrice (1e18), timestamp }
│ on-chain read
▼
HyperliquidEurUsdOracle (EUR/USD ← SlippageStorage; USDC/USD ← ChainlinkOracle)
│ slot 1 of the router
▼
OracleRouter (activeOracle = 1) ◄── ChainlinkOracle (slot 0, fallback)
│ IOracle.getEurUsdPrice()
▼
QuantillonVault (mint/redeem) + off-server watchdog (freezes on stale / breaker / basis blow-out)
HyperliquidEurUsdOracle
is IOracle (interface IHyperliquidOracle), modelled on StorkOracle. UUPS, AccessControl,
Pausable, TimeProvider-based time.
- EUR/USD:
slippageStorage.getSlippageBySource(sourceId)→midPrice(already 18 decimals) +timestamp(on-chain write time = staleness anchor). No scaling. - USDC/USD: delegated to
usdcSource(theChainlinkOracle) viagetUsdcUsdPrice(), in a try/catch so a USDC-feed failure can never block an EUR/USD read (falls back to(1e18, false)). - Validation:
maxPriceStaleness(state var, default 900s, ≤ 3600 hard cap), boundsminEurUsdPrice/maxEurUsdPrice(default 0.80–1.40e18),MAX_PRICE_DEVIATION5% circuit breaker,lastValidEurUsdPricefallback, pausable. getEurUsdPrice()returns(price, isValid). On circuit-breaker / paused / stale / out-of-bounds / over-deviation it returns(lastValidEurUsdPrice, false); a valid read advances the baseline. Afalseflag is a hard stop for the vault (mint/redeem revert) — never a stale price used for valuation.- Router compatibility: implements the four management selectors the router delegates to the active
oracle (
updatePriceBounds,updateUsdcTolerance,resetCircuitBreaker,triggerCircuitBreaker), so it slots into the (formerly Stork) slot 1 with noOracleRouterchange. - Source swap-ready:
updateSlippageSource(addr, sourceId)(ORACLE_MANAGER) repoints the EUR/USD source without touching the vault or router.
OracleRouter
Two slots: OracleType.CHAINLINK = 0, OracleType.MARKET = 1 (slot 1 = the swappable market oracle, currently Hyperliquid). Slot 1 was named STORK before router v1.1.0; the old storkOracle() getter remains as a deprecated alias of marketOracle().
_getActiveOracle() casts the active slot to IOracle and delegates all reads. Management:
switchOracle(type) and updateOracleAddresses(chainlink, slot1) — both ORACLE_MANAGER_ROLE.
QuantillonVault.oracle is the router, so the vault prices off whichever slot is active.
Role model
The Safe holds every governance role on each oracle (DEFAULT_ADMIN, ORACLE_MANAGER, EMERGENCY,
UPGRADER); the deployer holds none. By design of the deployment scripts, OracleRouter additionally
holds ORACLE_MANAGER_ROLE and EMERGENCY_ROLE on the market oracle — the active
HyperliquidEurUsdOracle (and the inert LighterEurUsdOracle, granted 2026-07-20) — which is what lets
the router's management passthroughs (updatePriceBounds, updateUsdcTolerance,
triggerCircuitBreaker, resetCircuitBreaker) work; it holds no role on ChainlinkOracle. Admin
operations are nevertheless normally done by the Safe directly on the oracle, not through the
router. Verified with hasRole reads on 2026-09-05.
| Holder | DEFAULT_ADMIN | ORACLE_MANAGER | EMERGENCY | UPGRADER |
|---|---|---|---|---|
Safe 0x1d7f… | ✓ | ✓ | ✓ | ✓ |
| OracleRouter | — | ✓ (market oracle only) | ✓ (market oracle only) | — |
Deployer 0x8DAD… | — | — | — | — |
SlippageStorage: Safe holds DEFAULT_ADMIN_ROLE / MANAGER_ROLE; WRITER_ROLE is held by the
off-chain publisher wallet and, currently, also by the deployer EOA. Treasury on all oracles + vault is
0x8DAD…098d1 (used only by emergency recovery).
Deployment & wiring
scripts/deployment/DeployHyperliquidOracle.s.sol:
HL_ORACLE_ACTION=deploy-onlydeploys the impl + ERC1967 proxy and runsinitialize(admin, slippageStorage, sourceId, usdcSource, treasury). SetORACLE_ADMINto the Safe so the new oracle matches the existing oracles' governance (Safe = all roles).SLIPPAGE_STORAGEis required.- Go-live (Safe txs):
updateOracleAddresses(chainlink, hlOracle)to set slot 1, thenswitchOracle(1). - Fallback any time: Safe
switchOracle(0)→ ChainlinkOracle.
Verification (forge 1.7.1 + Etherscan v2) needs the chainid in the verifier URL:
forge verify-contract <impl> src/oracle/HyperliquidEurUsdOracle.sol:HyperliquidEurUsdOracle --chain 8453 --verifier-url "https://api.etherscan.io/v2/api?chainid=8453" --etherscan-api-key $ETHERSCAN_API_KEY --constructor-args $(cast abi-encode "constructor(address)" <TimeProvider>).
Tests
test/HyperliquidEurUsdOracle.t.sol— unit (read, staleness, bounds, deviation, circuit breaker, pause, USDC delegation, source-revert fail-safe, config/access control).test/HyperliquidOracleRouterIntegration.t.sol— proves the adapter drops into the router's slot 1 unchanged (routing, bounds/circuit-breaker delegation, USDC pass-through, Chainlink fallback).
Safety summary
Every failure mode is fail-safe: a stale/invalid/out-of-band read returns isValid=false (vault
reverts), the off-server watchdog pauses the vault on staleness / circuit-break / a basis blow-out vs
Chainlink, and governance can fall back to Chainlink with one switchOracle(0).
Independent reference checks
The active market oracle can compare its publication with a fresh Chainlink EUR/USD probe. Reference age, normal-hours divergence, and the Friday 21:00 UTC through Sunday 21:00 UTC off-hours bound are governance parameters; invalid or stale references fail closed. Emergency baseline reseeding requires a fresh reference that passes the same checks and emits an on-chain event.
Execution and fallback boundaries
The router mid is a valuation reference, not a normal executable user quote. ExecutionPricing checks directional depth, observed capacity and source compatibility. Switching to Chainlink alone does not reopen minting. Its degraded redemption path still needs a valid reference, remains bounded by the last reporter-certified net directional capacity, and cannot bypass pause, liquidity or minimum-output checks. The dapp can apply stricter quote-availability gates than the contract fallback.