Oracles
Unified price feeds for all Lux DeFi protocols
Oracles
THE standard Oracle system for all Lux DeFi protocols - Perps, Lending, AMM, Flash Loans.
Architecture
┌─────────────────────────────────────────────────────────────────────────────────────────┐
│ ORACLE ARCHITECTURE │
├─────────────────────────────────────────────────────────────────────────────────────────┤
│ │
│ PRICE SOURCES (IOracleSource) │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │Chainlink │ │ Pyth │ │ TWAP │ │ DEX │ │OracleHub │ │
│ │ Adapter │ │ Adapter │ │ Source │ │Precompile│ │(written) │ │
│ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ └────┬─────┘ │
│ │ │ │ │ │ │
│ └─────────────┴─────────────┴─────────────┴─────────────┘ │
│ │ │
│ ┌──────────▼──────────┐ │
│ │ Oracle.sol │ ← THE interface for DeFi apps │
│ │ (aggregates all) │ │
│ └──────────┬──────────┘ │
│ │ │
│ ┌───────────────────────┼───────────────────────┐ │
│ │ │ │ │
│ ┌────────▼────────┐ ┌─────────▼────────┐ ┌─────────▼────────┐ │
│ │ Perps │ │ Markets │ │ Flash Loans │ │
│ │ VaultPriceFeed │ │ (Morpho-style) │ │ (Atomic ops) │ │
│ └─────────────────┘ └──────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────────────────────────┘
DEX Gateway (~/work/lux/dex) writes prices → OracleHub → read by Oracle.solCore Contracts
| Contract | Path | Purpose |
|---|---|---|
| Oracle | contracts/oracle/Oracle.sol | THE main oracle for all DeFi apps |
| OracleHub | contracts/oracle/OracleHub.sol | On-chain price hub (written by DEX) |
| ChainlinkAdapter | contracts/oracle/adapters/ChainlinkAdapter.sol | Chainlink price feeds |
| PythAdapter | contracts/oracle/adapters/PythAdapter.sol | Pyth Network feeds |
| TWAPSource | contracts/oracle/sources/TWAPSource.sol | AMM TWAP prices |
| DEXSource | contracts/oracle/sources/DEXSource.sol | DEX precompile (0x0400) |
Interfaces
| Interface | Path | Purpose |
|---|---|---|
| IOracle | contracts/oracle/IOracle.sol | Main oracle interface |
| IOracleSource | contracts/oracle/interfaces/IOracleSource.sol | Individual price source |
| IOracleWriter | contracts/oracle/interfaces/IOracleWriter.sol | Price writing (DEX) |
| IOracleStrategy | contracts/oracle/interfaces/IOracleStrategy.sol | Aggregation strategies |
Quick Start
import "@luxfi/contracts/oracle/IOracle.sol";
contract MyDeFiApp {
IOracle public oracle;
constructor(address _oracle) {
oracle = IOracle(_oracle);
}
// Simple price query
function getAssetPrice(address asset) public view returns (uint256) {
(uint256 price, ) = oracle.getPrice(asset);
return price; // 18 decimals, USD
}
// For perps: use maximize param
function getPerpsPrice(address asset, bool isLong) public view returns (uint256) {
return oracle.getPriceForPerps(asset, isLong);
}
// Batch query for gas efficiency
function getMultiplePrices(address[] calldata assets) public view returns (uint256[] memory) {
(uint256[] memory prices, ) = oracle.getPrices(assets);
return prices;
}
// Check oracle health before critical ops
function isOracleHealthy() public view returns (bool) {
(bool healthy, uint256 sources) = oracle.health();
return healthy && sources >= 2;
}
}IOracle Interface
interface IOracle {
// Core price functions
function getPrice(address asset) external view returns (uint256 price, uint256 timestamp);
function getPriceIfFresh(address asset, uint256 maxAge) external view returns (uint256 price);
function price(address asset) external view returns (uint256 price);
function isSupported(address asset) external view returns (bool);
// Batch operations (gas efficient)
function getPrices(address[] calldata assets) external view returns (uint256[] memory, uint256[] memory);
// Perps-specific
function getPriceForPerps(address asset, bool maximize) external view returns (uint256 price);
function isPriceConsistent(address asset, uint256 maxDeviationBps) external view returns (bool);
// Health monitoring
function health() external view returns (bool healthy, uint256 activeSourceCount);
function isCircuitBreakerTripped(address asset) external view returns (bool);
}Price Sources
1. Chainlink (Push Oracle)
import "@luxfi/contracts/integrations/oracles/ChainlinkAdapter.sol";
// Deploy
ChainlinkAdapter chainlink = new ChainlinkAdapter(
3600 // max 1 hour staleness
);
// Configure feeds
chainlink.setFeed(WETH, 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419); // ETH/USD
chainlink.setFeed(WBTC, 0xF4030086522a5bEEa4988F8cA5B36dbC97BeE88c); // BTC/USD
// Get price
(uint256 price, uint256 timestamp) = chainlink.getPrice(WETH);2. Pyth (Pull Oracle)
import "@luxfi/contracts/integrations/oracles/RedStoneAdapter.sol";
// Pyth price IDs
bytes32 constant ETH_USD = 0xff61491a931112ddf1bd8147cd1b641375f79f5825126d665480874634fd0ace;
bytes32 constant BTC_USD = 0xe62df6c8b4a85fe1a67db44dc12de5db330f7ac66b72dc658afedf0f4a415b43;
// Deploy
PythAdapter pyth = new PythAdapter(
0x4305FB66699C3B2702D4d05CF36551390A4c69C6, // Pyth contract
60 // max 60s staleness
);
// Configure feeds
pyth.setFeed(WETH, ETH_USD);
pyth.setFeed(WBTC, BTC_USD);
// Update prices (required before reading)
pyth.updatePrices{value: updateFee}(pythUpdateData);
// Get price
(uint256 price, uint256 timestamp) = pyth.getPrice(WETH);3. TWAP (AMM-based)
import "@luxfi/contracts/oracle/sources/TWAPSource.sol";
// Deploy
TWAPSource twap = new TWAPSource(
USDC, // quote token
6 // USDC decimals
);
// Configure pairs
twap.setPair(WETH, WETH_USDC_PAIR);
twap.setPair(WBTC, WBTC_USDC_PAIR);
// Get TWAP (30 min default)
(uint256 price, uint256 timestamp) = twap.getPrice(WETH);4. DEX Precompile (Native)
import "@luxfi/contracts/oracle/sources/DEXSource.sol";
// Deploy
DEXSource dex = new DEXSource(LUSD); // quote token
// Configure pools
dex.setPool(
WETH, // asset
3000, // fee tier (0.3%)
60, // tick spacing
address(0) // no hooks
);
// Get spot price from DEX precompile (0x0400)
(uint256 price, uint256 timestamp) = dex.getPrice(WETH);5. OracleHub (DEX-Written Prices)
The DEX gateway (~/work/lux/dex) writes prices to OracleHub:
import "@luxfi/contracts/oracle/OracleHub.sol";
// DEX gateway writes prices
oracleHub.writePrice(WETH, 4000e18, block.timestamp);
// Or with validator signatures (trustless)
oracleHub.writeQuorumPrice(signedUpdates, 3); // 3-of-5 quorum
// DeFi apps read via Oracle.sol
(uint256 price, ) = oracle.getPrice(WETH);Aggregation Strategies
import "@luxfi/contracts/oracle/interfaces/IOracleStrategy.sol";
// Available strategies
MedianStrategy median = new MedianStrategy(); // Default, manipulation-resistant
MeanStrategy mean = new MeanStrategy(); // Simple average
MinStrategy min = new MinStrategy(); // Conservative (liquidations)
MaxStrategy max = new MaxStrategy(); // Aggressive (borrowing limits)
// Set strategy on Oracle
oracle.setStrategy(IOracleStrategy(address(median)));Perps Integration
For perpetual futures, use getPriceForPerps with spread:
contract PerpsVault {
IOracle public oracle;
function openPosition(
address asset,
uint256 size,
bool isLong
) external {
// Use maximize=true for longs (worse entry price)
// Use maximize=false for shorts
uint256 entryPrice = oracle.getPriceForPerps(asset, isLong);
// Check price consistency before large trades
require(oracle.isPriceConsistent(asset, 100), "Price deviation >1%");
// Open position at entry price...
}
function liquidate(address user, address asset) external {
// Check circuit breaker before liquidation
require(!oracle.isCircuitBreakerTripped(asset), "Circuit breaker active");
// Get liquidation price
(uint256 price, ) = oracle.getPrice(asset);
// Execute liquidation...
}
}Lending Integration
For Morpho-style lending markets:
contract LendingMarket {
IOracle public oracle;
function getCollateralValue(
address asset,
uint256 amount
) public view returns (uint256 valueUsd) {
(uint256 price, uint256 timestamp) = oracle.getPrice(asset);
// Validate freshness for critical operations
require(block.timestamp - timestamp < 1 hours, "Stale price");
valueUsd = (amount * price) / 1e18;
}
function isLiquidatable(address user) public view returns (bool) {
// Batch query for gas efficiency
address[] memory assets = getUserAssets(user);
(uint256[] memory prices, ) = oracle.getPrices(assets);
// Calculate health factor...
}
}Flash Loan Safety
Oracle provides atomic price guarantees within transactions:
contract FlashLoanArbitrage {
IOracle public oracle;
function executeArbitrage(
address asset,
uint256 amount
) external {
// Get price at start (view function = atomic)
(uint256 startPrice, ) = oracle.getPrice(asset);
// Execute flash loan and swaps...
// Same price guaranteed within tx
(uint256 endPrice, ) = oracle.getPrice(asset);
assert(startPrice == endPrice); // Always true within same block
}
}DEX Integration
The DEX gateway (~/work/lux/dex) provides:
| Source | Weight | Update Interval | Description |
|---|---|---|---|
| X-Chain | 2.0 | 50ms | Native DEX orderbook (highest trust) |
| C-Chain AMM | 1.8 | 100ms | Liquid tokens (LETH, LBTC, etc.) |
| Zoo Chain | 1.7 | 100ms | ZOO token pairs |
| A-Chain | 1.5 | 100-200ms | Validator attestations |
| Pyth | 1.2 | Real-time | WebSocket streaming |
| Chainlink | 1.0 | ~1s | Reference only |
The DEX aggregates all sources and writes to OracleHub via:
writePrice()- Keeper-based updateswriteSignedPrice()- Single validator signaturewriteQuorumPrice()- Multi-validator consensus
Circuit Breakers
Automatic protection against price manipulation:
// Oracle configuration
oracle.setCircuitBreakerConfig(
1000, // 10% max price change
5 minutes // cooldown period
);
// Check before critical operations
if (oracle.isCircuitBreakerTripped(asset)) {
revert("Circuit breaker active, try later");
}
// Manual reset by guardian
oracle.resetCircuitBreaker(asset);Health Monitoring
// Check overall health
(bool healthy, uint256 sourceCount) = oracle.health();
require(healthy && sourceCount >= 2, "Oracle unhealthy");
// Check individual source health
(bool srcHealthy, uint256 lastHb) = chainlinkAdapter.health();Gas Optimization
// BAD: Multiple individual calls
uint256 ethPrice = oracle.price(WETH);
uint256 btcPrice = oracle.price(WBTC);
uint256 luxPrice = oracle.price(LUX);
// GOOD: Single batch call
address[] memory assets = new address[](3);
assets[0] = WETH;
assets[1] = WBTC;
assets[2] = LUX;
(uint256[] memory prices, ) = oracle.getPrices(assets);Deployment
// 1. Deploy strategies
MedianStrategy medianStrategy = new MedianStrategy();
// 2. Deploy main Oracle
Oracle oracle = new Oracle(address(medianStrategy));
// 3. Deploy and add sources
ChainlinkAdapter chainlink = new ChainlinkAdapter(3600);
PythAdapter pyth = new PythAdapter(PYTH_CONTRACT, 60);
TWAPSource twap = new TWAPSource(USDC, 6);
DEXSource dex = new DEXSource(LUSD);
OracleHub hub = new OracleHub();
oracle.addSource(IOracleSource(address(chainlink)));
oracle.addSource(IOracleSource(address(pyth)));
oracle.addSource(IOracleSource(address(twap)));
oracle.addSource(IOracleSource(address(dex)));
oracle.addSource(IOracleSource(address(hub)));
// 4. Configure
oracle.setConfig(
1 hours, // maxAge
500, // maxDeviationBps (5%)
2 // minSources
);
// 5. Grant DEX gateway writer access to OracleHub
hub.addWriter(DEX_GATEWAY);Security Considerations
| Risk | Mitigation |
|---|---|
| Stale prices | Freshness checks with maxAge |
| Flash loan manipulation | TWAP over 30+ minutes |
| Single source failure | Multi-source aggregation (minSources=2) |
| Price manipulation | Median aggregation + deviation checks |
| Oracle downtime | Multiple sources + fallback logic |
| Large price swings | Circuit breakers (10% max change) |
Related
- Perpetuals - Uses
getPriceForPerpswith spread - Lending - Uses oracles for collateral valuation
- AMM - TWAP source integration
- DEX Precompile - Native on-chain pricing at 0x0400