Skip to content

Commit

Permalink
Add stride helpers and rework ibc (#3)
Browse files Browse the repository at this point in the history
* add gaia, stride functionality and format/lint

* small fixes

* change key for stride

* use main branch

* add gaia to config

* rename to plural

* add upload with cache

* rename and use id for stride

* fix stride denom

* rework ibc

* line

* fix channel mistake

* undo

* fix ibc

* add inserting ibc_denom once we get it

* add stride to test_context
  • Loading branch information
keyleu authored Jul 9, 2024
1 parent a876264 commit c4277a9
Show file tree
Hide file tree
Showing 16 changed files with 519 additions and 154 deletions.
15 changes: 8 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,16 @@ version = "0.1.0"
edition = "2021"

[dependencies]
localic-std = { git = "https://github.com/strangelove-ventures/interchaintest", rev = "f326371" }
cosmwasm-std = "1.5.4"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = "1.0.117"
thiserror = "1.0"
localic-std = { git = "https://github.com/strangelove-ventures/interchaintest", branch = "main" }
cosmwasm-std = "1.5.5"
serde = { version = "1.0.204", features = ["derive"] }
serde_json = "1.0.120"
thiserror = "1.0.61"
derive_builder = "0.20.0"
log = "0.4.21"
log = "0.4.22"
astroport = "5.1.0"
reqwest = { version = "0.11.20", features = ["rustls-tls"] }
sha2 = "0.10.8"

[dev-dependencies]
env_logger = "0.11.3"
env_logger = "0.11.3"
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ For a full list of builder calls available under the `TestContextBuilder`, see [
* `.with_api_url(api_url: impl Into<String>)`
* `.with_chain(chain: ConfigChain)`
* The `TestContext` is not configured to use any chains by default. Calling this builder method adds a `ConfigChain`, which grants the `TestContext` access to that chain's related helper functions. These helper functions will error without access to their requisite chains.
* `.with_transfer_channel(chain_a: impl Into<String>, chain_b: impl Into<String>)`
* Registers a transfer channel ID upon building the `TestContext` between chain A and chain B. Assumes that chain A and chain B are chains registered with `.with_chain`
* `.with_transfer_channels(chain_a: impl Into<String>, chain_b: impl Into<String>)`
* Registers transfer channels IDs upon building the `TestContext` between chain A and chain B. Assumes that chain A and chain B are chains registered with `.with_chain`
* `.with_unwrap_raw_logs(unwrap_logs: bool)`
* Enables or disables log unwrapping - an assertion upon every `tx_*` helper function's execution that ensures no errors are present in logs returned by the transaction

Expand Down
13 changes: 4 additions & 9 deletions examples/neutron_osmosis.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use localic_utils::{error::Error as LocalIcUtilsError, ConfigChainBuilder, TestContextBuilder};
use localic_utils::{ConfigChainBuilder, TestContextBuilder};
use std::error::Error;

const ACC_0_ADDR: &str = "osmo1hj5fveer5cjtn4wd6wstzugjfdxzl0xpwhpz63";
Expand All @@ -15,8 +15,7 @@ fn main() -> Result<(), Box<dyn Error>> {
.with_artifacts_dir("contracts")
.with_chain(ConfigChainBuilder::default_neutron().build()?)
.with_chain(ConfigChainBuilder::default_osmosis().build()?)
.with_transfer_channel("osmosis", "neutron")
.with_transfer_channel("neutron", "osmosis")
.with_transfer_channels("osmosis", "neutron")
.build()?;

ctx.build_tx_create_tokenfactory_token()
Expand Down Expand Up @@ -44,12 +43,8 @@ fn main() -> Result<(), Box<dyn Error>> {
.with_amount(1000000)
.send()?;

let ibc_bruhtoken = ctx.get_ibc_denom(&bruhtoken, "neutron", "osmosis").ok_or(
LocalIcUtilsError::MissingContextVariable(format!("ibc_denom::{}", &bruhtoken)),
)?;
let ibc_neutron = ctx.get_ibc_denom("untrn", "neutron", "osmosis").ok_or(
LocalIcUtilsError::MissingContextVariable(format!("ibc_denom::{}", "untrn")),
)?;
let ibc_bruhtoken = ctx.get_ibc_denom(&bruhtoken, "neutron", "osmosis");
let ibc_neutron = ctx.get_ibc_denom("untrn", "neutron", "osmosis");

// Create an osmosis pool
ctx.build_tx_create_osmo_pool()
Expand Down
10 changes: 9 additions & 1 deletion src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,15 @@ pub const TRANSFER_PORT: &str = "transfer";
/// File extension for WASM files
pub const WASM_EXTENSION: &str = "wasm";

// Gaia chain info
pub const GAIA_CHAIN_ID: &str = "localcosmos-1";
pub const GAIA_CHAIN_NAME: &str = "gaia";
pub const GAIA_CHAIN_PREFIX: &str = "cosmos";
pub const GAIA_CHAIN_DENOM: &str = "uatom";
pub const GAIA_CHAIN_ADMIN_ADDR: &str = "cosmos1hj5fveer5cjtn4wd6wstzugjfdxzl0xpxvjjvr";

/// Neutron chain info
pub const NEUTRON_CHAIN_ID: &str = "localneutron-1";
pub const NEUTRON_CHAIN_NAME: &str = "neutron";
pub const NEUTRON_CHAIN_PREFIX: &str = "neutron";
pub const NEUTRON_CHAIN_DENOM: &str = "untrn";
Expand Down Expand Up @@ -50,10 +58,10 @@ pub const PRICE_ORACLE_NAME: &str = "price_oracle";
pub const LOCAL_IC_API_URL: &str = "http://localhost:42069/";

/// Builder defautls
pub const ADMIN_KEY: &str = "admin";
pub const DEFAULT_KEY: &str = "acc0";
pub const DEFAULT_TRANSFER_PORT: &str = "transfer";
pub const DEFAULT_AUCTION_LABEL: &str = "auction";
pub const DEFAULT_NEUTRON_CHAIN_ID: &str = "localneutron-1";

pub const TX_HASH_QUERY_RETRIES: u16 = 5;
pub const TX_HASH_QUERY_PAUSE_SEC: u64 = 2;
32 changes: 24 additions & 8 deletions src/types/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::{
GAIA_CHAIN_ADMIN_ADDR, GAIA_CHAIN_DENOM, GAIA_CHAIN_ID, GAIA_CHAIN_NAME, GAIA_CHAIN_PREFIX,
NEUTRON_CHAIN_ID,
};

use super::super::{
DEFAULT_NEUTRON_CHAIN_ID, NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_DENOM, NEUTRON_CHAIN_NAME,
NEUTRON_CHAIN_PREFIX, OSMOSIS_CHAIN_ADMIN_ADDR, OSMOSIS_CHAIN_DENOM, OSMOSIS_CHAIN_ID,
OSMOSIS_CHAIN_NAME, OSMOSIS_CHAIN_PREFIX, STRIDE_CHAIN_ADMIN_ADDR, STRIDE_CHAIN_DENOM,
STRIDE_CHAIN_ID, STRIDE_CHAIN_NAME, STRIDE_CHAIN_PREFIX,
NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_DENOM, NEUTRON_CHAIN_NAME, NEUTRON_CHAIN_PREFIX,
OSMOSIS_CHAIN_ADMIN_ADDR, OSMOSIS_CHAIN_DENOM, OSMOSIS_CHAIN_ID, OSMOSIS_CHAIN_NAME,
OSMOSIS_CHAIN_PREFIX, STRIDE_CHAIN_ADMIN_ADDR, STRIDE_CHAIN_DENOM, STRIDE_CHAIN_ID,
STRIDE_CHAIN_NAME, STRIDE_CHAIN_PREFIX,
};
use derive_builder::Builder;
use serde::Deserialize;
Expand All @@ -13,9 +18,9 @@ pub struct ChainsVec {
pub chains: Vec<ConfigChain>,
}

impl Into<Vec<ConfigChain>> for ChainsVec {
fn into(self) -> Vec<ConfigChain> {
self.chains
impl From<ChainsVec> for Vec<ConfigChain> {
fn from(val: ChainsVec) -> Vec<ConfigChain> {
val.chains
}
}

Expand All @@ -31,11 +36,22 @@ pub struct ConfigChain {
}

impl ConfigChainBuilder {
pub fn default_gaia() -> Self {
Self {
denom: Some(String::from(GAIA_CHAIN_DENOM)),
debugging: Some(true),
chain_id: Some(String::from(GAIA_CHAIN_ID)),
chain_name: Some(String::from(GAIA_CHAIN_NAME)),
chain_prefix: Some(String::from(GAIA_CHAIN_PREFIX)),
admin_addr: Some(String::from(GAIA_CHAIN_ADMIN_ADDR)),
}
}

pub fn default_neutron() -> Self {
Self {
denom: Some(String::from(NEUTRON_CHAIN_DENOM)),
debugging: Some(true),
chain_id: Some(String::from(DEFAULT_NEUTRON_CHAIN_ID)),
chain_id: Some(String::from(NEUTRON_CHAIN_ID)),
chain_name: Some(String::from(NEUTRON_CHAIN_NAME)),
chain_prefix: Some(String::from(NEUTRON_CHAIN_PREFIX)),
admin_addr: Some(String::from(NEUTRON_CHAIN_ADMIN_ADDR)),
Expand Down
87 changes: 78 additions & 9 deletions src/types/ibc.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize)]
pub struct Trace {
pub channel_id: String,
pub port_id: String,
pub base_denom: String,
pub dest_denom: String,
}
use crate::TRANSFER_PORT;
use serde::Deserialize;
use sha2::{Digest, Sha256};

#[derive(Deserialize)]
pub struct Channel {
Expand All @@ -24,3 +18,78 @@ pub struct Counterparty {
pub channel_id: String,
pub port_id: String,
}

#[derive(Debug)]
pub struct DenomTrace {
pub path: String,
pub base_denom: String,
}

impl DenomTrace {
pub fn ibc_denom(&self) -> String {
if !self.path.is_empty() {
return format!("ibc/{}", self.hash());
}
self.base_denom.clone()
}

fn hash(&self) -> String {
let trace = format!("{}/{}", self.path, self.base_denom);
let mut hasher = Sha256::new();
hasher.update(trace.as_bytes());
format!("{:x}", hasher.finalize()).to_uppercase()
}
}

pub fn get_prefixed_denom(port_id: String, channel_id: String, native_denom: String) -> String {
format!("{}/{}/{}", port_id, channel_id, native_denom)
}

pub fn get_multihop_ibc_denom(native_denom: &str, channel_trace: Vec<&str>) -> String {
let mut port_channel_trace = vec![];

for channel in channel_trace {
port_channel_trace.push(TRANSFER_PORT);
port_channel_trace.push(channel);
}

let prefixed_denom = format!("{}/{}", port_channel_trace.join("/"), native_denom);

let src_denom_trace = parse_denom_trace(prefixed_denom);
src_denom_trace.ibc_denom()
}

pub fn parse_denom_trace(raw_denom: String) -> DenomTrace {
let denom_split = raw_denom.split('/').collect::<Vec<&str>>();

if denom_split[0] == raw_denom {
return DenomTrace {
path: "".to_string(),
base_denom: raw_denom.to_string(),
};
}

let (path, base_denom) = extract_path_and_base_from_full_denom(denom_split);

DenomTrace { path, base_denom }
}

pub fn extract_path_and_base_from_full_denom(full_denom_items: Vec<&str>) -> (String, String) {
let mut path: Vec<&str> = Vec::new();
let mut base_denom: Vec<&str> = Vec::new();

let length = full_denom_items.len();
let mut i = 0;
while i < length {
if i < length - 1 && length > 2 {
path.push(full_denom_items[i]);
path.push(full_denom_items[i + 1]);
} else {
base_denom = full_denom_items[i..].to_vec();
break;
}
i += 2;
}

(path.join("/"), base_denom.join("/"))
}
96 changes: 34 additions & 62 deletions src/utils/fixtures.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use crate::{
types::ibc::{get_prefixed_denom, parse_denom_trace},
TRANSFER_PORT,
};

use super::{
super::{
error::Error, types::ibc::Trace, AUCTION_CONTRACT_NAME, FACTORY_NAME, NEUTRON_CHAIN_NAME,
OSMOSIS_CHAIN_NAME, PAIR_NAME, PRICE_ORACLE_NAME, STABLE_PAIR_NAME,
TX_HASH_QUERY_PAUSE_SEC, TX_HASH_QUERY_RETRIES,
error::Error, AUCTION_CONTRACT_NAME, FACTORY_NAME, NEUTRON_CHAIN_NAME, OSMOSIS_CHAIN_NAME,
PAIR_NAME, PRICE_ORACLE_NAME, STABLE_PAIR_NAME, TX_HASH_QUERY_PAUSE_SEC,
TX_HASH_QUERY_RETRIES,
},
test_context::TestContext,
};
Expand Down Expand Up @@ -37,7 +42,7 @@ impl TestContext {

let raw_log = logs.as_ref().and_then(|raw_log| raw_log.as_str()).unwrap();

if &raw_log == &"" {
if raw_log.is_empty() {
return Ok(());
}

Expand Down Expand Up @@ -89,7 +94,7 @@ impl TestContext {
Ok(CosmWasm::new_from_existing(
&neutron.rb,
Some(contract_info.artifact_path.clone()),
Some(contract_info.code_id.clone()),
Some(contract_info.code_id),
Some(contract_info.address.clone()),
))
}
Expand All @@ -102,7 +107,7 @@ impl TestContext {
let contract_addr = neutron
.contract_addrs
.get(PRICE_ORACLE_NAME)
.and_then(|addrs| addrs.get(0))
.and_then(|addrs| addrs.first())
.cloned()
.ok_or(Error::MissingContextVariable(String::from(
"contract_addrs::price_oracle",
Expand Down Expand Up @@ -165,7 +170,7 @@ impl TestContext {
let artifacts_path = self.artifacts_dir.as_str();

Ok(contract_addrs
.into_iter()
.iter()
.map(|addr| {
CosmWasm::new_from_existing(
&neutron.rb,
Expand All @@ -187,7 +192,7 @@ impl TestContext {
) -> Result<CosmWasm, Error> {
let factories = self.get_astroport_factory()?;
let factory = factories
.get(0)
.first()
.ok_or(Error::MissingContextVariable(String::from(FACTORY_NAME)))?;

let pair_info = factory.query_value(&serde_json::json!(
Expand Down Expand Up @@ -284,71 +289,38 @@ impl TestContext {
}

/// Gets the IBC denom for a base denom given a src and dest chain.
pub fn get_ibc_denom(
&mut self,
base_denom: impl AsRef<str>,
src_chain: impl Into<String>,
dest_chain: impl Into<String>,
) -> Option<String> {
let src_chain_string = src_chain.into();
let dest_chain_string = dest_chain.into();
let base_denom_str = base_denom.as_ref();

pub fn get_ibc_denom(&mut self, base_denom: &str, src_chain: &str, dest_chain: &str) -> String {
if let Some(denom) = self
.ibc_denoms
.get(&(base_denom_str.into(), dest_chain_string.clone()))
.get(&(base_denom.to_string(), dest_chain.to_string()))
{
return Some(denom.clone());
return denom.clone();
}

let dest_chain = self.get_chain(&dest_chain_string);

let channel = self
.transfer_channel_ids
.get(&(dest_chain_string.clone(), src_chain_string.clone()))?;
let trace = format!("transfer/{}/{}", channel, base_denom_str);

let resp = dest_chain
.rb
.q(&format!("q ibc-transfer denom-hash {trace}"), true);
let channel_id = self
.get_transfer_channels()
.src(dest_chain)
.dest(src_chain)
.get();

let ibc_denom = format!(
"ibc/{}",
serde_json::from_str::<Value>(&resp.get("text")?.as_str()?)
.ok()?
.get("hash")?
.as_str()?
let prefixed_denom = get_prefixed_denom(
TRANSFER_PORT.to_string(),
channel_id.to_string(),
base_denom.to_string(),
);

let src_denom_trace = parse_denom_trace(prefixed_denom);
let ibc_denom = src_denom_trace.ibc_denom();

self.ibc_denoms.insert(
(base_denom_str.into(), dest_chain_string.clone()),
(base_denom.to_string(), dest_chain.to_string()),
ibc_denom.clone(),
);
self.ibc_denoms
.insert((ibc_denom.clone(), src_chain_string), base_denom_str.into());

Some(ibc_denom)
}
self.ibc_denoms.insert(
(ibc_denom.clone(), src_chain.to_string()),
base_denom.to_string(),
);

/// Gets the IBC channel and port for a given denom.
pub fn get_ibc_trace(
&mut self,
base_denom: impl Into<String> + AsRef<str>,
src_chain: impl Into<String> + AsRef<str>,
dest_chain: impl Into<String> + AsRef<str>,
) -> Option<Trace> {
let dest_denom =
self.get_ibc_denom(base_denom.as_ref(), src_chain.as_ref(), dest_chain.as_ref())?;

let channel = self
.transfer_channel_ids
.get(&(src_chain.into(), dest_chain.into()))?;

Some(Trace {
port_id: "transfer".to_owned(),
channel_id: channel.to_owned(),
base_denom: base_denom.into(),
dest_denom,
})
ibc_denom
}
}
Loading

0 comments on commit c4277a9

Please sign in to comment.