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:

  1. Check to make sure the currency-pair exists within the x/oracle and x/marketmap modules.
  2. Check to make sure the currency-pair is enabled within the x/marketmap (otherwise, it won’t update)
  3. 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
  4. 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(&currency_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.