Band Oracle with Cadence
The Band Protocol Oracle contract enables Flow blockchain applications to access real-time price data from the Band Protocol Oracle network. The oracle provides a comprehensive set of cryptocurrency and fiat currency price quotes from the Band Standard Dataset, making them available to any Cadence application, contract, or transaction.
Contract Addresses
Network | Address | CLI | Explorer |
---|---|---|---|
Testnet | 0x9fb6606c300b5051 | View Contract | |
Mainnet | 0x6801a6222ebf784a | View Contract |
Supported Symbols
Cryptocurrency Pairs (against USD)
- Major: ETH, FLOW, USDC, USDT, WBTC, BNB, XRP, ADA, DOGE, POL (MATIC)
- Layer 1: SOL, DOT, AVAX, ATOM, XLM, TRX, SUI
- DeFi: AAVE, LINK, CRV, OP, UNI, SUSHI, CAKE, DYDX, 1INCH, BAT
- Others: LTC, SHIB, DAI, FTM
Fiat Currency Pairs (against USD)
- Asian: KRW, INR, HKD, TWD, THB, JPY, MYR, PHP, CNY, SGD
- European: PLN, CZK, EUR, GBP, CHF, RUB, SEK, TRY
- Americas: BRL, CAD
- Oceanic: AUD, NZD
How It Works
Architecture
The Band Oracle contract maintains a decentralized price feed system with three key components:
-
Data Storage: Price data is stored in a contract-level dictionary
symbolsRefData: {String: RefData}
where each symbol maps to its latest price information. -
Data Updates: Authorized BandChain relayers continuously update price data from the Band Protocol network to keep prices current.
-
Data Access: Any user or contract can query the latest price data through public functions, enabling real-time price integrations.
Data Structure
Price data is stored using the RefData
struct:
_10access(all) struct RefData {_10 // USD-rate, multiplied by 1e9_10 access(all) var rate: UInt64_10 // UNIX epoch when data was last resolved_10 access(all) var timestamp: UInt64_10 // BandChain request identifier for this data_10 access(all) var requestID: UInt64_10}
When querying prices, you receive a ReferenceData
struct:
_10access(all) struct ReferenceData {_10 // Rate as integer multiplied by 1e18_10 access(all) var integerE18Rate: UInt256_10 // Rate as a fixed-point decimal_10 access(all) var fixedPointRate: UFix64_10 // Timestamp of base symbol data_10 access(all) var baseTimestamp: UInt64_10 // Timestamp of quote symbol data_10 access(all) var quoteTimestamp: UInt64_10}
Data Normalization
All price data is stored with a USD conversion rate. When you query for price conversions between two non-USD symbols, the contract derives the rate from their respective USD rates. For example, to get ETH/EUR, the contract calculates: (ETH/USD) / (EUR/USD)
.
Features
Price Queries
- Query any supported symbol pair in real-time
- Get both integer (e18 precision) and fixed-point decimal rates
- Access timestamp information to verify data freshness
- Track BandChain request IDs for transparency
Fee Structure
- Configurable fee system for oracle usage (currently set to zero)
- Fee collected in FLOW tokens
- Query current fee using
BandOracle.getFee()
Event Monitoring
The contract emits events to notify applications of updates:
_10// Emitted when symbol prices are updated_10access(all) event BandOracleSymbolsUpdated(_10 symbols: [String],_10 relayerID: UInt64,_10 requestID: UInt64_10)_10_10// Emitted when a symbol is removed_10access(all) event BandOracleSymbolRemoved(symbol: String)
Usage Guide
Basic Price Query (Transaction)
To query price data from a transaction:
_32import "BandOracle"_32import "FlowToken"_32import "FungibleToken"_32_32transaction(baseSymbol: String, quoteSymbol: String) {_32_32 let payment: @{FungibleToken.Vault}_32_32 prepare(acct: auth(BorrowValue) &Account) {_32 // Borrow reference to user's FLOW vault_32 let vaultRef = acct.storage.borrow<auth(FungibleToken.Withdraw) &FlowToken.Vault>(_32 from: /storage/flowTokenVault_32 ) ?? panic("Cannot borrow reference to signer's FLOW vault")_32_32 // Withdraw payment for oracle fee_32 self.payment <- vaultRef.withdraw(amount: BandOracle.getFee())_32 }_32_32 execute {_32 // Get reference data_32 let priceData = BandOracle.getReferenceData(_32 baseSymbol: baseSymbol,_32 quoteSymbol: quoteSymbol,_32 payment: <- self.payment_32 )_32_32 log("Rate (fixed-point): ".concat(priceData.fixedPointRate.toString()))_32 log("Rate (integer e18): ".concat(priceData.integerE18Rate.toString()))_32 log("Base timestamp: ".concat(priceData.baseTimestamp.toString()))_32 log("Quote timestamp: ".concat(priceData.quoteTimestamp.toString()))_32 }_32}
Example: ETH/USD Price
_10// Get ETH price in USD_10let priceData = BandOracle.getReferenceData(_10 baseSymbol: "ETH",_10 quoteSymbol: "USD",_10 payment: <- flowPayment_10)_10// priceData.fixedPointRate contains ETH price in USD
Example: Cross-Currency Conversion
_10// Get EUR price in JPY_10let priceData = BandOracle.getReferenceData(_10 baseSymbol: "EUR",_10 quoteSymbol: "JPY",_10 payment: <- flowPayment_10)_10// priceData.fixedPointRate contains EUR/JPY exchange rate
Contract Integration
Here's how to integrate the oracle into your smart contract:
_44import "BandOracle"_44import "FlowToken"_44import "FungibleToken"_44_44access(all) contract MyDeFiContract {_44_44 // Store a vault to pay for oracle fees_44 access(self) let oracleFeeVault: @{FungibleToken.Vault}_44_44 access(all) fun getTokenPriceInUSD(tokenSymbol: String): UFix64 {_44 // Withdraw payment for oracle_44 let payment <- self.oracleFeeVault.withdraw(_44 amount: BandOracle.getFee()_44 )_44_44 // Query the oracle_44 let priceData = BandOracle.getReferenceData(_44 baseSymbol: tokenSymbol,_44 quoteSymbol: "USD",_44 payment: <- payment_44 )_44_44 return priceData.fixedPointRate_44 }_44_44 access(all) fun swapTokens(amount: UFix64, maxPrice: UFix64) {_44 // Get current price_44 let currentPrice = self.getTokenPriceInUSD(tokenSymbol: "ETH")_44_44 // Verify price is acceptable_44 if currentPrice > maxPrice {_44 panic("Price too high")_44 }_44_44 // Proceed with swap logic..._44 }_44_44 init() {_44 // Initialize vault for oracle fees_44 self.oracleFeeVault <- FlowToken.createEmptyVault(_44 vaultType: Type<@FlowToken.Vault>()_44 )_44 }_44}
Best Practices
1. Listen for Price Updates
Monitor the BandOracleSymbolsUpdated
event to keep your contract's stored prices up-to-date:
_10// Listen for this event in your application_10access(all) event BandOracleSymbolsUpdated(_10 symbols: [String],_10 relayerID: UInt64,_10 requestID: UInt64_10)
When you detect an update for symbols your app uses, trigger a transaction to refresh your stored prices.
Advanced Features
Converting Between Number Formats
The contract provides a utility function to convert between integer and fixed-point representations:
_10// Convert e18 integer to fixed-point decimal_10let fixedPoint = BandOracle.e18ToFixedPoint(rate: integerE18Rate)
Fee Management
For contract administrators, the oracle supports dynamic fee configuration:
_10// Query current fee_10let currentFee = BandOracle.getFee()_10_10// Fee can be updated by the fee collector (admin only)_10// feeCollector.setFee(fee: 0.001) // 0.001 FLOW per query
Resources
Note: The oracle currently charges no fees for usage, but this may change in the future. Always check BandOracle.getFee()
before querying to ensure your contract has sufficient FLOW tokens allocated.