DeploySEALED

○?▓▓*╱%$□░╳◆!╱╱~!╳╱□╲▓◇▫╳*#□▫╱~?■%░□~◆█▫░@^*▒#◇▫$&##@■▪$◇*╳?*▪▪▪

Transaction ID

Timestamp

Nov 13, 2025, 08:40:59 PM UTC
3mo ago

Block Height

132,672,779

Computation

0

Execution Fee

0.00000949 FLOW

Transaction Summary

Deploy

Contract deployment

Contract deployment

Script Arguments

0nameString
OracleAggregator
1codeString
import PriceOracle from 0x17ae3b1b0b0d50db import BandPriceOracle from 0x17ae3b1b0b0d50db import PoolPriceOracle from 0x17ae3b1b0b0d50db import SimplePriceOracle from 0x17ae3b1b0b0d50db /// OracleAggregator: Multi-oracle price aggregation with waterfall fallback /// /// This contract implements a robust multi-oracle strategy that queries multiple /// price sources in priority order, ensuring maximum reliability and token coverage. /// /// **Waterfall Strategy**: /// 1. **Band Protocol** (Primary) - For major tokens (FLOW, BTC, ETH, USDC, etc.) /// - Decentralized oracle network /// - Real-time updates /// - High reliability /// /// 2. **Pool Price Oracle** (Secondary) - For DEX-specific tokens (Froth, etc.) /// - Direct liquidity pool queries /// - Real-time pool prices /// - Supports any token with a pool /// /// 3. **Simple Price Oracle** (Tertiary) - For manual overrides/emergencies /// - Admin-controlled prices /// - Fallback when others unavailable /// - Testing and development /// /// **Example Usage**: /// - FLOW: Band Protocol → Pool (if Band fails) → Simple (emergency) /// - Froth: Pool Price → Simple (if pool query fails) /// - New Token: Pool Price → Simple (manual price) /// access(all) contract OracleAggregator: PriceOracle { /// Price data structure (matches PriceOracle interface) /// Uses AnyStruct to handle different oracle implementations access(all) struct PriceData { access(all) let symbol: String access(all) let price: UFix64 access(all) let timestamp: UFix64 access(all) let confidence: UFix64? init(symbol: String, price: UFix64, timestamp: UFix64, confidence: UFix64?) { self.symbol = symbol self.price = price self.timestamp = timestamp self.confidence = confidence } } /// Oracle priority levels access(all) enum OraclePriority: UInt8 { access(all) case band // Highest priority access(all) case pool // Medium priority access(all) case simple // Lowest priority (fallback) } /// Oracle source tracking for transparency access(all) struct PriceResult { access(all) let priceData: PriceData access(all) let source: String // "Band", "Pool", "Simple" access(all) let priority: OraclePriority init(priceData: PriceData, source: String, priority: OraclePriority) { self.priceData = priceData self.source = source self.priority = priority } } /// Storage paths access(all) let OracleStoragePath: StoragePath access(all) let OraclePublicPath: PublicPath access(all) let AdminStoragePath: StoragePath /// Oracle configuration access(all) struct OracleConfig { access(all) let useBand: Bool access(all) let usePool: Bool access(all) let useSimple: Bool init(useBand: Bool, usePool: Bool, useSimple: Bool) { self.useBand = useBand self.usePool = usePool self.useSimple = useSimple } } /// Active oracle configuration access(all) var oracleConfig: OracleConfig /// Symbol-specific oracle preferences /// Allows configuring which oracle to prefer for specific tokens /// Example: "FROTH" -> OraclePriority.pool (skip Band, go straight to Pool) access(self) var symbolPreferences: {String: OraclePriority} /// Price cache (combined from all oracles) access(self) var aggregateCache: {String: PriceResult} /// Cache expiry (60 seconds - aligned with Band) access(all) var cacheExpiry: UFix64 /// Events access(all) event PriceFetched( symbol: String, price: UFix64, source: String, priority: UInt8, timestamp: UFix64 ) access(all) event OracleFallback( symbol: String, failedSource: String, fallbackSource: String ) access(all) event SymbolPreferenceSet( symbol: String, preferredOracle: UInt8 ) access(all) event OracleConfigUpdated( useBand: Bool, usePool: Bool, useSimple: Bool ) access(all) event CacheExpiryUpdated(newExpiry: UFix64) access(all) event AdminTransferred(oldAdmin: Address, newAdmin: Address, transferredBy: Address) /// Admin resource for configuration management /// Only the contract deployer receives this capability access(all) resource Admin { /// Set oracle preference for a specific symbol /// Example: setSymbolPreference("FROTH", OraclePriority.pool) /// This will make Froth always query Pool first, skipping Band access(all) fun setSymbolPreference(symbol: String, priority: OraclePriority) { pre { symbol.length > 0: "Symbol cannot be empty" } OracleAggregator.symbolPreferences[symbol] = priority emit SymbolPreferenceSet(symbol: symbol, preferredOracle: priority.rawValue) } /// Remove symbol preference (revert to waterfall strategy) access(all) fun removeSymbolPreference(symbol: String) { OracleAggregator.symbolPreferences.remove(key: symbol) } /// Enable/disable oracle sources /// At least one oracle must remain enabled access(all) fun configureOracles(useBand: Bool, usePool: Bool, useSimple: Bool) { pre { useBand || usePool || useSimple: "At least one oracle must be enabled" } OracleAggregator.oracleConfig = OracleConfig( useBand: useBand, usePool: usePool, useSimple: useSimple ) emit OracleConfigUpdated( useBand: useBand, usePool: usePool, useSimple: useSimple ) } /// Set cache expiry time (how long to cache aggregated prices) access(all) fun setCacheExpiry(_ newExpiry: UFix64) { pre { newExpiry > 0.0 && newExpiry <= 3600.0: "Cache expiry must be between 0 and 1 hour" } OracleAggregator.cacheExpiry = newExpiry emit CacheExpiryUpdated(newExpiry: newExpiry) } } /// Oracle implementation access(all) resource Oracle: PriceOracle.Oracle { /// Get current price using waterfall strategy access(all) fun getPrice(symbol: String): AnyStruct? { if let result = OracleAggregator.fetchPriceWithFallback(symbol: symbol, maxAge: 300.0) { return result.priceData as AnyStruct } return nil } /// Get price with custom staleness check access(all) fun getPriceWithMaxAge(symbol: String, maxAge: UFix64): AnyStruct? { if let result = OracleAggregator.fetchPriceWithFallback(symbol: symbol, maxAge: maxAge) { return result.priceData as AnyStruct } return nil } /// Check if token is supported by any oracle access(all) fun supportsToken(symbol: String): Bool { // Check if any oracle can provide a price for this symbol if OracleAggregator.oracleConfig.useBand { if let _ = BandPriceOracle.getPriceWithMaxAge(symbol: symbol, maxAge: 300.0) { return true } } if OracleAggregator.oracleConfig.usePool { if let _ = PoolPriceOracle.getPrice(symbol: symbol) { return true } } if OracleAggregator.oracleConfig.useSimple { if let _ = SimplePriceOracle.getPrice(symbol: symbol) { return true } } return false } /// Get all supported tokens (union of all oracles) access(all) fun getSupportedTokens(): [String] { let symbols: {String: Bool} = {} if OracleAggregator.oracleConfig.useBand { // Band supported tokens would be listed here // For now, add common ones symbols["FLOW"] = true symbols["BTC"] = true symbols["ETH"] = true symbols["USDC"] = true symbols["USDT"] = true } if OracleAggregator.oracleConfig.usePool { // Use public function to get supported tokens let poolSymbols = PoolPriceOracle.getSupportedTokens() for symbol in poolSymbols { symbols[symbol] = true } } if OracleAggregator.oracleConfig.useSimple { // SimplePriceOracle has all symbols in its prices dict // Would need to access it - simplified for now } return symbols.keys } } /// Fetch price with waterfall fallback strategy access(all) fun fetchPriceWithFallback(symbol: String, maxAge: UFix64): PriceResult? { let currentTime = getCurrentBlock().timestamp // Check aggregate cache first if let cachedResult = self.aggregateCache[symbol] { let cacheAge = currentTime - cachedResult.priceData.timestamp if cacheAge <= self.cacheExpiry { return cachedResult // Cache hit } } // Check for symbol-specific preference let preference = self.symbolPreferences[symbol] // If preference set, try that oracle first if preference != nil { let result = self.tryOracle(priority: preference!, symbol: symbol, maxAge: maxAge) if result != nil { return self.cacheAndReturn(result!) } } // Waterfall: Try each oracle in priority order // 1. Band Protocol (if enabled) if self.oracleConfig.useBand { let result = self.tryOracle(priority: OraclePriority.band, symbol: symbol, maxAge: maxAge) if result != nil { return self.cacheAndReturn(result!) } } // 2. Pool Price (if enabled) if self.oracleConfig.usePool { let result = self.tryOracle(priority: OraclePriority.pool, symbol: symbol, maxAge: maxAge) if result != nil { if self.oracleConfig.useBand { // Emit fallback event if we tried Band first emit OracleFallback( symbol: symbol, failedSource: "Band", fallbackSource: "Pool" ) } return self.cacheAndReturn(result!) } } // 3. Simple Oracle (if enabled) if self.oracleConfig.useSimple { let result = self.tryOracle(priority: OraclePriority.simple, symbol: symbol, maxAge: maxAge) if result != nil { emit OracleFallback( symbol: symbol, failedSource: self.oracleConfig.usePool ? "Pool" : "Band", fallbackSource: "Simple" ) return self.cacheAndReturn(result!) } } // All oracles failed return nil } /// Try to fetch price from a specific oracle access(self) fun tryOracle(priority: OraclePriority, symbol: String, maxAge: UFix64): PriceResult? { var priceData: PriceData? = nil var source = "" switch priority { case OraclePriority.band: // Convert BandPriceOracle.PriceData to OracleAggregator.PriceData if let bandPriceAny = BandPriceOracle.getPriceWithMaxAge(symbol: symbol, maxAge: maxAge) { if let bandPrice = bandPriceAny as? BandPriceOracle.PriceData { priceData = PriceData( symbol: bandPrice.symbol, price: bandPrice.price, timestamp: bandPrice.timestamp, confidence: bandPrice.confidence ) } } source = "Band" case OraclePriority.pool: // Convert PoolPriceOracle.PriceData to OracleAggregator.PriceData if let poolPriceAny = PoolPriceOracle.getPrice(symbol: symbol) { if let poolPrice = poolPriceAny as? PoolPriceOracle.PriceData { priceData = PriceData( symbol: poolPrice.symbol, price: poolPrice.price, timestamp: poolPrice.timestamp, confidence: poolPrice.confidence ) } } source = "Pool" case OraclePriority.simple: // Convert SimplePriceOracle.PriceData to OracleAggregator.PriceData if let simplePrice = SimplePriceOracle.getPriceWithMaxAge(symbol: symbol, maxAge: maxAge) { priceData = PriceData( symbol: simplePrice.symbol, price: simplePrice.price, timestamp: simplePrice.timestamp, confidence: simplePrice.confidence ) } source = "Simple" } if priceData == nil { return nil } return PriceResult( priceData: priceData!, source: source, priority: priority ) } /// Cache result and emit event access(self) fun cacheAndReturn(_ result: PriceResult): PriceResult { // Cache the result self.aggregateCache[result.priceData.symbol] = result // Emit event emit PriceFetched( symbol: result.priceData.symbol, price: result.priceData.price, source: result.source, priority: result.priority.rawValue, timestamp: result.priceData.timestamp ) return result } /// Public convenience function access(all) fun getPrice(symbol: String): PriceData? { let result = self.fetchPriceWithFallback(symbol: symbol, maxAge: 300.0) return result?.priceData } /// Get price with source information access(all) fun getPriceWithSource(symbol: String): PriceResult? { return self.fetchPriceWithFallback(symbol: symbol, maxAge: 300.0) } /// Create a new Oracle resource access(all) fun createOracle(): @Oracle { return <- create Oracle() } init() { self.OracleStoragePath = /storage/OracleAggregator self.OraclePublicPath = /public/OracleAggregator self.AdminStoragePath = /storage/OracleAggregatorAdmin // Enable all oracles by default self.oracleConfig = OracleConfig(useBand: true, usePool: true, useSimple: true) // Cache settings self.cacheExpiry = 60.0 // 60 seconds // Initialize storage self.symbolPreferences = {} self.aggregateCache = {} // Example: Set Pool as preferred oracle for Flow ecosystem tokens // self.symbolPreferences["FROTH"] = OraclePriority.pool // self.symbolPreferences["EMERALD"] = OraclePriority.pool self.account.storage.save(<-create Admin(), to: self.AdminStoragePath) } }

Cadence Script

1transaction(name: String, code: String ) {
2		prepare(signer: auth(AddContract) &Account) {
3			signer.contracts.add(name: name, code: code.utf8 )
4		}
5	}