# Band Protocol Secret Network Developer Documentation
In addition to data native to the Secret Network developers also have access to various cryptocurrency price data provided by [Band Protocol](https://bandprotocol.com/)'s oracle.
## Standard Reference Dataset Contract Info
### Data Available
The price data originates from [data requests](https://github.com/bandprotocol/bandchain/wiki/System-Overview#oracle-data-request) made on BandChain and then sent to Band's `std_reference_basic` contract on Secret network 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:
For example
- BTC/USD
- ETH/USD
- BTC/ETH
The prices themselves are the median of the values retrieved by BandChain's validators from many sources including [CoinGecko](https://www.coingecko.com/en/api), [CryptoCompare](https://min-api.cryptocompare.com/), [Bibox]("https://rapidapi.com/blog/directory/bibox/"), [Binance]("https://www.binance.com/en/binance-api"),[Bravenewcoin]("https://docs.bravenewcoin.com/"), [CoinMarketcap](https://pro.coinmarketcap.com/api/v1), [HuobiPro](https://huobiapi.github.io/docs/spot/v1/en/#change-log), [CoinBasePro](https://docs.cloud.coinbase.com/exchange/docs), [Kraken](https://docs.kraken.com/rest/), [Bitfinex](https://docs.bitfinex.com/docs), [Bittrex](https://bittrex.github.io/api/v3), [OKX](https://www.okx.com/docs-v5/en/), [FTX](https://docs.ftx.com/), [HitBTC](https://api.hitbtc.com/). The data request is then made by executing Band's aggregator oracle script, the code of which you can view on Band's [mainnet](https://cosmoscan.io/oracle-script/3). 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.
### Standard Reference Dataset Contract Price Update Process
For the ease of development, the Band Foundation will be maintaining and updating the `std_reference_basic` 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.
## Retrieving the Price Data
The code below shows an example of a simple price database contract on Secret network which retrieve price data from Band's `std_reference_proxy` contract and store it in the contract's state. This following diagram shows the working steps of a message `SavePrice` only, which will be explain on the next section.
```shell=
(1) Send message "SavePrice"
|
|
v
===================
| |
| simple price db |
| |
===================
| ^
|(2) |(5)
|Ask proxy |Retrive the returned
|contract for |result and then save to the state
|price |
v |
=================== (3) Ask base contract =====================
| |------------------------->| |
| std ref proxy | | std ref basic |
| |<-------------------------| |
=================== (4) Result =====================
```
The contract is able to store exchange rate of any price pair that available on the `std_reference_basic` contract. For more information on what oracle scripts are and how data requests work on BandChain in general, please see their [wiki](https://github.com/bandprotocol/bandchain/wiki/System-Overview#oracle-data-request) and [developer documentation](https://docs.bandchain.org/dapp-developers/requesting-data-from-bandchain)
## Code Breakdown
Now we are going to breakdown the `simple price database` contract. The contract can be broken down into 4 sections which are `messages`, `state`, `struct types`, `contract logic`.
#### Messages
The `InitMsg` is used to initialize state variables of the contract. This message only have `initial_oracle_ref` field which is the address of the oracle contract.
```rust
pub struct InitMsg {
pub initial_oracle_ref: HumanAddr,
}
```
The `HandleMsg` is used for state transition of the contract. This message has 2 sub messages, `SetOracleRef` and `SavePrice`. `SetOracleRef` is used when the owner want to change the reference of oracle contract. `SavePrice` is used when the owner want to consume price data from the oracle contract and then save it into the local state of this contract.
```rust
pub enum HandleMsg {
// new_oracle_ref: a new oracle address to be set
SetOracleRef { new_oracle_ref: HumanAddr },
// symbol: a symbol that will be used to ask the oracle to get the price
SavePrice { symbol: String }
}
```
The `QueryMsg` is used for contract's state reading. This message has 3 sub messages, `Owner`,`OracleRef`,`GetPrice`. The messages will be used to read the address of the owner, read the address of current oracle, read the the price of a specific symbol, respectively.
```rust
pub enum QueryMsg {
// query owner address
Owner {},
// query oracle address
OracleRef {},
// query price that has been saved
GetPrice { symbol: String }
}
```
The `QueryExtMsg` is used only when a call is made across a contract, which arises internally and is not caused by a direct call. This message only have 1 sub message `GetReferenceData`. `GetReferenceData` is used while trying to get a price from the oracle/`std_reference_proxy`
```rust
pub enum QueryExtMsg {
GetReferenceData {
base_symbol: String,
quote_symbol: String,
}
}
```
This section consists of two functions, `get_reference_data` and `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.
#### State
The state of this contract only contain 2 local state variables and 1 mapping (`string => Uint128`) which are `owner`, `oracle_ref` and `price` (mapping).
#### Struct Types
This contract only have 1 custom struct which will be used only when getting price from the oracle (message `SavePrice`).
```rust
pub struct ReferenceData {
pub rate: Uint128,
pub last_updated_base: u64,
pub last_updated_quote: u64,
}
```
#### Contract Logic
The contract logic can be divided into 4 parts which are `init`, `query`, `cross-contrct query` and `handle`.
The `init` part is only used at the contract initialization step. It basically set the owner of the contract and then set the reference of the oracle.
```rust
pub fn init<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
msg: InitMsg,
) -> StdResult<InitResponse> {
owner(&mut deps.storage).save(&deps.api.canonical_address(&env.message.sender)?)?;
oracle_ref(&mut deps.storage).save(&deps.api.canonical_address(&msg.initial_oracle_ref)?)?;
Ok(InitResponse::default())
}
```
The `query` part is consist of 3 sub queries which are `query_owner`, `query_oracle_ref` and `query_price`. They are basically read the state and then return the result back to the querier.
```rust
fn query_owner<S: Storage, A: Api, Q: Querier>(deps: &Extern<S, A, Q>) -> StdResult<CanonicalAddr> {
owner_read(&deps.storage)
.load()
.map_err(|_| StdError::generic_err("OWNER_NOT_INITIALIZED"))
}
fn query_oracle_ref<S: Storage, A: Api, Q: Querier>(deps: &Extern<S, A, Q>) -> StdResult<CanonicalAddr> {
oracle_ref_read(&deps.storage)
.load()
.map_err(|_| StdError::generic_err("ORACLE_REF_NOT_INITIALIZED"))
}
fn query_price<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
symbol: String,
) -> StdResult<Uint128> {
match price_read(&deps.storage).get(&symbol.as_bytes()) {
Some(data) => {
Ok(bincode::deserialize(&data).unwrap())
},
_ => Err(StdError::generic_err(format!(
"PRICE_NOT_AVAILABLE_FOR_KEY:{}",
symbol
))),
}
}
```
The `cross-contrct query` is only used when handle the message `SavePrice`. It basically send a query message `GetReferenceData` to the oracle contract and then return the struct `ReferenceData`.
```rust
// cross-contract query
fn query_reference_data<S: Storage, A: Api, Q: Querier>(
deps: &Extern<S, A, Q>,
base_symbol: String,
quote_symbol: String,
) -> StdResult<ReferenceData> {
Ok(deps.querier.custom_query::<QueryMsg, ReferenceData>(
&WasmQuery::Smart {
contract_addr: deps.api.human_address(&query_oracle_ref(deps)?)?,
msg: to_binary(&QueryExtMsg::GetReferenceData {
base_symbol,
quote_symbol,
})?,
}
.into(),
)?)
}
```
The `handle` part is consist of 2 sub handling which are `try_set_oracle_ref` and `try_set_price`. They both verify ownership before allowing sender to proceed. The `try_set_oracle_ref` will only save the `new_oracle_ref` into the state. For the `try_set_price`, it will make a `cross-contract query` to the oracle contract by using an input symbol as a parameter. After getting back the result from the oracle, the result price will be saved into the state.
```rust
pub fn try_set_oracle_ref<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
new_oracle_ref: HumanAddr,
) -> StdResult<HandleResponse> {
let owner_addr = owner(&mut deps.storage).load()?;
if deps.api.canonical_address(&env.message.sender)? != owner_addr {
return Err(StdError::generic_err("NOT_AUTHORIZED"));
}
oracle_ref(&mut deps.storage).save(&deps.api.canonical_address(&new_oracle_ref)?)?;
Ok(HandleResponse::default())
}
pub fn try_set_price<S: Storage, A: Api, Q: Querier>(
deps: &mut Extern<S, A, Q>,
env: Env,
symbol: String,
) -> StdResult<HandleResponse> {
let owner_addr = owner(&mut deps.storage).load()?;
if deps.api.canonical_address(&env.message.sender)? != owner_addr {
return Err(StdError::generic_err("NOT_AUTHORIZED"));
}
let reference_data = query_reference_data(deps, symbol.clone(), "USD".into())?;
price(&mut deps.storage).set(symbol.as_bytes(), &bincode::serialize(&reference_data.rate).unwrap());
Ok(HandleResponse::default())
}
```
## List of Band oracle contracts on Secret Network
### Secret Network Mainnet
| Contract | Address |
| ------------------- | :------------------------------------------: |
| std_reference_proxy | secret1ezamax2vrhjpy92fnujlpwfj2dpredaafss47k |
### Secret Network Pulsar-2 Testnet
| Contract | Address |
| ------------------- | :------------------------------------------: |
| std_reference_proxy | secret14swdnnllsfvtnvwmtvnvcj2zu0njsl9cdkk5xp |