The price data originates from data requests made on BandChain. Band’s std_reference SCORE on ICON then retrieves and stores the results of those requests. Specifically, the following price pairs are available to be read from the std_reference_proxy contract:
The prices themselves are the median of the values retrieved by BandChain’s validators from many sources including CoinGecko, CryptoCompare, Binance, CoinMarketcap, HuobiPro, CoinBasePro, Kraken, Bitfinex, Bittrex, BITSTAMP, OKEX, FTX, HitBTC, ItBit, Bithumb, CoinOne. The data request is then made by executing Band’s aggregator oracle script, the code of which you can view on Band’s proof-of-authority mainnet. Along with the price data, developers will also have access to the latest timestamp the price was updated.
These parameters are intended to act as security parameters to help anyone using the data to verify that the data they are using is what they expect and, perhaps more importantly, actually valid.
For the ease of development, the Band Foundation will be maintaining and updating the std_reference contract with the latest price data. In the near future, we will be releasing guides on how developers can create similar contracts themselves to retrieve data from Band’s oracle.
=================== | | | simple price db | | | =================== | ^ |(1) |(4) |Asking for |Return |price data |result | | v | =================== (2) Ask =================== | |-------------->| | | std ref proxy | | std ref | | |<--------------| | =================== (3) Result ===================
The contract is able to store exchange rate of any price pair that available on the std_reference contract. For more information on what oracle scripts are and how data requests work on BandChain in general, please see their wiki and developer documentation
from iconservice import * TAG = "SimplePriceDB" # Interface of StdReferenceProxy class IStdReferenceProxy(InterfaceScore): @interface def get_reference_data(self, _base: str, _quote: str) -> dict: pass @interface def get_reference_data_bulk(self, _bases: str, _quotes: str) -> list: pass # SimplePriceDB contract class SimplePriceDB(IconScoreBase): def __init__(self, db: IconScoreDatabase) -> None: super().__init__(db) self.std_reference_proxy_address = VarDB( "std_reference_proxy_address", db, value_type=Address ) self.prices = DictDB("prices", db, value_type=int) def on_install(self, _proxy: Address) -> None: super().on_install() self.set_proxy(_proxy) def on_update(self) -> None: super().on_update() # Get price of the given pair multiply by 1e18. # For example "BTC/ETH" -> 30.09 * 1e18. @external(readonly=True) def get_price(self, _pair: str) -> int: return self.prices[_pair] # Sets the proxy contract address, which can only be set by the owner. @external def set_proxy(self, _proxy: Address) -> None: if self.msg.sender != self.owner: self.revert("NOT_AUTHORIZED") self.std_reference_proxy_address.set(_proxy) # This function accepts the string coin pairs such as "BTC/ETH". # And then pass it to the std_reference_proxy. After receiving the output # from the std_reference_proxy, the exchange rate of the input pair # is recorded into the state. @external def set_single(self, _pair: str) -> None: proxy = self.create_interface_score( self.std_reference_proxy_address.get(), IStdReferenceProxy ) base, quote = _pair.split("/") result = proxy.get_reference_data(base, quote) self.prices[_pair] = result["rate"] # This function accepts the string of the encoding of an array of coin pairs # such as '["BTC/ETH", "ETH/USD", "USDT/USD"]'. And then transform the format # of the input to pass it to the std_reference_proxy. After receiving the output # from the std_reference_proxy, each pair's rate is recorded into the state. @external def set_multiple(self, _json_pairs: str) -> None: proxy = self.create_interface_score( self.std_reference_proxy_address.get(), IStdReferenceProxy ) pairs = json_loads(_json_pairs) bases, quotes = [json_dumps(x) for x in zip(*[pair.split("/") for pair in pairs])] results = proxy.get_reference_data_bulk(bases, quotes) if len(pairs) != len(results): self.revert("LEN_PAIRS_MUST_EQUAL_TO_LEN_RESULTS") for pair, result in zip(pairs, results): self.prices[pair] = result["rate"]
The example code above can be broken down into two sections: defining the interface for the
IStdReferenceProxy and the actual
SimplePriceDB SCORE code itself.
This section consists of two functions,
get_reference_data_bulk. This is the interface that we’ll use to query price from Band oracle for the latest price of a token or a bunch of tokens.
SimplePriceDB then contains the main logic of our SCORE. It’s purpose will be to store the latest price of tokens.
The actual price data query can then be called through the
get_price function. Before we can call the method, however, we need to first set the address of the
std_reference_proxy. This is done by calling the
set_proxy method or
constructor. After that the price should be set by calling
set_single function will simply calling
std_reference_proxy with base symbol and quote symbol. It then extract the exchange rate from the result and save to the state.
set_multiple function converts the input into an array of base symbol and quote symbol arrays. After that it will call
std_reference_proxy with base symbols and quote symbols. It then extract the exchange rates from the results and save all of them to the state.
The full source code for the
SimplePriceDB score can be found in this repo along with the JSON for sending the
set_multiple. The score itself is also deployed to the testnet at address cxd8b7e45dad9a111254f1d3168931e9ca562bc534.
Updated 4 months ago