#module #pool #query #message #cosmwasm #osmosis #protobuf

osmosis-std

Standard library for Osmosis with CosmWasm support included

30 releases (11 breaking)

1.19.0 Sep 25, 2023
0.24.0 Apr 17, 2024
0.22.0 Jan 15, 2024
0.21.0 Dec 15, 2023
0.1.2 Jul 25, 2022

#1409 in Magic Beans

Download history 1129/week @ 2023-12-23 1351/week @ 2023-12-30 3325/week @ 2024-01-06 2751/week @ 2024-01-13 2909/week @ 2024-01-20 2766/week @ 2024-01-27 3530/week @ 2024-02-03 5048/week @ 2024-02-10 3324/week @ 2024-02-17 3586/week @ 2024-02-24 3066/week @ 2024-03-02 4460/week @ 2024-03-09 2943/week @ 2024-03-16 2353/week @ 2024-03-23 2339/week @ 2024-03-30 1885/week @ 2024-04-06

10,200 downloads per month
Used in 55 crates (18 directly)

MIT/Apache

1.5MB
43K SLoC

osmosis-std

osmosis-std on crates.io Docs

Osmosis's proto-generated types and helpers for interacting with the appchain. Compatible with CosmWasm contract.

CosmWasm stargate message and stargate query

You can find all types and querier generated from osmosis's protobuf in their respective module in osmosis_std. To understand how each module works, please look at the osmosis documentation.

Full working example contract can be found here.

Publishing Osmosis' message from CosmWasm Contract

use cosmwasm_std::{CosmosMsg, Response, Env};
use osmosis_std::types::osmosis::tokenfactory::v1beta1::MsgCreateDenom;

# type ContractError = cosmwasm_std::StdError;
// ..

pub fn try_create_denom(env: Env, subdenom: String) -> Result<Response, ContractError> {
    let sender = env.contract.address.into();

    // construct message and convert them into cosmos message
    // (notice `CosmosMsg` type and `.into()`)
    let msg_create_denom: CosmosMsg = MsgCreateDenom { sender, subdenom }.into();

    Ok(Response::new()
        .add_message(msg_create_denom)
        .add_attribute("method", "try_create_denom"))
}

Querying Osmosis' module

Each module has its own querier that derived from protobuf service definition that can be found here.

To avoid non-determinism in stargate queries, only some of them are whitelisted, you can find the list here.

use cosmwasm_std::{Deps, Env, StdResult};
use osmosis_std::types::osmosis::tokenfactory::v1beta1::{TokenfactoryQuerier, QueryDenomsFromCreatorResponse};

// ..

fn query_creator_denoms(deps: Deps, env: Env) -> StdResult<QueryDenomsFromCreatorResponse> {
    // create `TokenfactoryQuerier`
    let tokenfactory = TokenfactoryQuerier::new(&deps.querier);

    // `TokenfactoryQuerier` has all the fns for querying the module
    let res = tokenfactory.denoms_from_creator(env.contract.address.into())?;

    Ok(QueryDenomsFromCreatorResponse { denoms: res.denoms })
}

Querying Pool

When querying pool related values, eg. Gamm::pool, you might find that return type contains Any. It's a cosmos' way to implement polymorphism in protobuf.

https://github.com/osmosis-labs/osmosis/blob/f024498f1e8e0d2a1fe259cd9cc4223803fea0cd/proto/osmosis/gamm/v1beta1/query.proto#L82-L84

message QueryPoolResponse {
  google.protobuf.Any pool = 1 [ (cosmos_proto.accepts_interface) = "PoolI" ];
}

This is needed due to osmosis supporting multiple pool types which will be added in the future.

For that matter, osmosis-std provides TryFrom trait for all possible Any used in all query responses in this crate.

That means the following code works:

use prost::DecodeError;
use cosmwasm_std::{Deps, StdResult, StdError};
use osmosis_std::types::osmosis::gamm::v1beta1::GammQuerier;

fn query_pool(
    deps: &Deps,
    pool_id: u64,
) -> StdResult<osmosis_std::types::osmosis::gamm::v1beta1::Pool> {
    let res = GammQuerier::new(&deps.querier).pool(pool_id)?;
    res.pool
        .ok_or_else(|| StdError::not_found("pool"))?
        .try_into() // convert `Any` to `osmosis_std::types::osmosis::gamm::v1beta1::Pool`
        .map_err(|e: DecodeError| StdError::parse_err(
            "osmosis_std::types::osmosis::gamm::v1beta1::Pool",
            e
        ))
}

Or if later you want to support multiple pool type

use prost::{DecodeError, Message};
use cosmwasm_std::{Deps, StdResult, StdError};
use osmosis_std::types::osmosis::gamm::v1beta1::GammQuerier;

enum Pool {
    Balancer(osmosis_std::types::osmosis::gamm::v1beta1::Pool),
    StableSwap(osmosis_std::types::osmosis::gamm::poolmodels::stableswap::v1beta1::Pool),
}

impl TryFrom<osmosis_std::shim::Any> for Pool {
    type Error = StdError;

    fn try_from(value: osmosis_std::shim::Any) -> Result<Self, Self::Error> {
        if let Ok(pool) = osmosis_std::types::osmosis::gamm::v1beta1::Pool::decode(value.value.as_slice()) {
            return Ok(Pool::Balancer(pool));
        }
        if let Ok(pool) = osmosis_std::types::osmosis::gamm::poolmodels::stableswap::v1beta1::Pool::decode(value.value.as_slice()) {
            return Ok(Pool::StableSwap(pool));
        }

        Err(StdError::parse_err(
            "Pool",
            "Unmatched pool: must be either `Balancer` or `StableSwap`."
        ))
    }
}

fn query_pool(
    deps: &Deps,
    pool_id: u64,
) -> StdResult<Pool> {
    let res = GammQuerier::new(&deps.querier).pool(pool_id)?;
    res.pool
        .ok_or_else(|| StdError::not_found("pool"))?
        .try_into() // convert `Any` to `Pool`
}

When translate to rust, especially with CosmWasm, it can get tricky if we want to also support json (de)serialization. It could erase type url information from serialized json as for current implementation..

Non-CosmWasm Client

(WIP)

Dependencies

~5–7MB
~146K SLoC