CosmWasm
Querying Connect prices using CosmWasm and Neutron SDK
Building with Connect? Join our Discord!
Summary
Connect prices are stored on-chain in x/oracle
, enabling price data to be locally accessible via CosmWasm smart contracts. x/marketmap
has configuration information that may also be helpful in your development.
Here’s how to get started:
How to access Connect prices safely
There are a number of highly-recommended checks we suggest developers use before accessing a Connect price. These include:
- Check to make sure the currency-pair exists within the
x/oracle
andx/marketmap
modules. - Check to make sure the currency-pair is
enabled
within thex/marketmap
(otherwise, it won’t update) - Check to make sure the
block_timestamp
of the last updated is less than 20 seconds from the most recent committed block timestamp. I.e.current_block_timestamp - block_timestamp < 20s
- Check to make sure the price is not returning
nil
(i.e. has not been initialized)
Example
The snippets below are not a complete implementation. Please check out the complete smart contract example for a complete implementation.
Install dependencies and import the libraries
Add the following libraries to your dependencies section:
[dependencies]
cosmwasm-std = "1.3.1"
# Other standard dependencies...
# TODO: update to "neuton-sdk" version once released
neutron-sdk = { package = "neutron-sdk", git = "https://github.com/neutron-org/neutron-sdk", branch = "feat/sdk-50" }
Import the libraries:
use neutron_sdk::bindings::marketmap::query::{
MarketMapQuery, MarketMapResponse, MarketResponse,
};
use neutron_sdk::bindings::oracle::types::CurrencyPair;
use neutron_sdk::bindings::{msg::NeutronMsg, query::NeutronQuery};
use neutron_sdk::bindings::oracle::query::{
GetAllCurrencyPairsResponse, GetPriceResponse, GetPricesResponse, OracleQuery,
};
Getting all supported markets on Connect, and ensuring an individual market is included
// create a CurrencyPair object
let currency_pair: CurrencyPair = CurrencyPair{
base: base_symbol.clone(), quote: quote_currency.clone(),
};
// fetch all supported currency pairs in x/oracle module
let oracle_currency_pairs_query: OracleQuery = OracleQuery::GetAllCurrencyPairs{};
let oracle_currency_pairs_response: GetAllCurrencyPairsResponse = deps.querier.query(
&oracle_currency_pairs_query.into(),
)?;
if oracle_currency_pairs_response.currency_pairs.contains(¤cy_pair) == false {
StdError::generic_err(format!(
"Market {base_symbol}, {quote_currency} not found in x/oracle module"
));
}
// fetch all supported currency pairs in x/marketmap module
let marketmap_currency_pairs_query: MarketMapQuery = MarketMapQuery::MarketMap{};
let marketmap_currency_pairs_response: MarketMapResponse = deps.querier.query(
&marketmap_currency_pairs_query.into(),
)?;
if marketmap_currency_pairs_response.market_map.markets.contains_key(&base_symbol.clone()) == false {
StdError::generic_err(format!(
"Market {base_symbol}, {quote_currency} not found in x/marketmap module"
));
}
Checking if a market is enabled
within x/marketmap
// fetch market for currency_pair in x/marketmap module
let marketmap_market_query: MarketMapQuery = MarketMapQuery::Market{
currency_pair: currency_pair.clone(),
};
let marketmap_market_response: MarketResponse = deps.querier.query(
&marketmap_market_query.into(),
)?;
// check if currency_pair is enabled
if marketmap_market_response.market.ticker.enabled == false {
StdError::generic_err(format!(
"Market {base_symbol}, {quote_currency} not enabled in x/marketmap module"
));
}
Checking if block_timestamp
is recent enough
// get current_block_height
let current_block_height: u64 = env.block.height;
// fetch price for currency_pair from x/oracle module
let oracle_price_query: OracleQuery = OracleQuery::GetPrice{
currency_pair: currency_pair.clone(),
};
let oracle_price_response: GetPriceResponse = deps.querier.query(
&oracle_price_query.into(),
)?;
// check if block_height is not too old
if (current_block_height - oracle_price_response.price.block_height) > max_blocks_old.u64() {
StdError::generic_err(format!(
"Market {base_symbol}, {quote_currency} price is older than {max_blocks_old} blocks"
));
}
Nil-checking a price response
Connect may sometimes return a “nil” Price if it has not been written to. This means within the GetPriceResponse
, the price will have Nonce = 0
To check for a nil price, see if Nonce == 0
in the GetPrice
response.
// fetch price for currency_pair from x/oracle module
let oracle_price_query: OracleQuery = OracleQuery::GetPrice{
currency_pair: currency_pair.clone(),
};
let oracle_price_response: GetPriceResponse = deps.querier.query(
&oracle_price_query.into(),
)?;
if oracle_price_response.nonce == 0 {
StdError::generic_err(format!(
"Market {base_symbol}, {quote_currency} price is nil"
));
}
Fetching an individual price
// fetch price for currency_pair from x/oracle module
let oracle_price_query: OracleQuery = OracleQuery::GetPrice{
currency_pair: currency_pair.clone(),
};
let oracle_price_response: GetPriceResponse = deps.querier.query(
&oracle_price_query.into(),
)?;
let market_price: Int128 = oracle_price_response.price.price;
Fetching a set of prices
Fetching multiple prices at once requires using OracleQuery::GetPrices { currency_pair_ids: Vec<String> }
in place of OracleQuery::GetPrice { currency_pair: CurrencyPair }
. Apply the same logic from the instructions above.
For a complete example see the full example.