Skip to main content

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

NetworkAddressCLIExplorer
Testnet0x9fb6606c300b5051View Contract
Mainnet0x6801a6222ebf784aView 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:

  1. Data Storage: Price data is stored in a contract-level dictionary symbolsRefData: {String: RefData} where each symbol maps to its latest price information.

  2. Data Updates: Authorized BandChain relayers continuously update price data from the Band Protocol network to keep prices current.

  3. 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:


_10
pub struct RefData {
_10
// USD-rate, multiplied by 1e9
_10
pub var rate: UInt64
_10
// UNIX epoch when data was last resolved
_10
pub var timestamp: UInt64
_10
// BandChain request identifier for this data
_10
pub var requestID: UInt64
_10
}

When querying prices, you receive a ReferenceData struct:


_10
pub struct ReferenceData {
_10
// Rate as integer multiplied by 1e18
_10
pub var integerE18Rate: UInt256
_10
// Rate as a fixed-point decimal
_10
pub var fixedPointRate: UFix64
_10
// Timestamp of base symbol data
_10
pub var baseTimestamp: UInt64
_10
// Timestamp of quote symbol data
_10
pub 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
_10
pub event BandOracleSymbolsUpdated(
_10
symbols: [String],
_10
relayerID: UInt64,
_10
requestID: UInt64
_10
)
_10
_10
// Emitted when a symbol is removed
_10
pub event BandOracleSymbolRemoved(symbol: String)

Usage Guide

Basic Price Query (Transaction)

To query price data from a transaction:


_32
import "BandOracle"
_32
import "FlowToken"
_32
import "FungibleToken"
_32
_32
transaction(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
_10
let 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
_10
let 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:


_44
import "BandOracle"
_44
import "FlowToken"
_44
import "FungibleToken"
_44
_44
pub contract MyDeFiContract {
_44
_44
// Store a vault to pay for oracle fees
_44
access(self) let oracleFeeVault: @{FungibleToken.Vault}
_44
_44
pub 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
pub 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
_10
pub event BandOracleSymbolsUpdated(
_10
symbols: [String],
_10
relayerID: UInt64,
_10
requestID: UInt64
_10
)

When you detect an update for symbols your dapp 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
_10
let fixedPoint = BandOracle.e18ToFixedPoint(rate: integerE18Rate)

Fee Management

For contract administrators, the oracle supports dynamic fee configuration:


_10
// Query current fee
_10
let 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.