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.