diff --git a/examples/contracts_osmosis/astroport_factory_osmosis.wasm b/examples/contracts_osmosis/astroport_factory_osmosis.wasm new file mode 100644 index 0000000..9088be9 Binary files /dev/null and b/examples/contracts_osmosis/astroport_factory_osmosis.wasm differ diff --git a/examples/contracts_osmosis/astroport_maker_osmosis.wasm b/examples/contracts_osmosis/astroport_maker_osmosis.wasm new file mode 100644 index 0000000..9e82da1 Binary files /dev/null and b/examples/contracts_osmosis/astroport_maker_osmosis.wasm differ diff --git a/examples/contracts_osmosis/astroport_native_coin_registry.wasm b/examples/contracts_osmosis/astroport_native_coin_registry.wasm new file mode 100644 index 0000000..7b7e8d9 Binary files /dev/null and b/examples/contracts_osmosis/astroport_native_coin_registry.wasm differ diff --git a/examples/contracts_osmosis/astroport_pcl_osmo.wasm b/examples/contracts_osmosis/astroport_pcl_osmo.wasm new file mode 100644 index 0000000..e78713c Binary files /dev/null and b/examples/contracts_osmosis/astroport_pcl_osmo.wasm differ diff --git a/examples/contracts_osmosis/auction.wasm b/examples/contracts_osmosis/auction.wasm new file mode 100644 index 0000000..c6b59bf Binary files /dev/null and b/examples/contracts_osmosis/auction.wasm differ diff --git a/examples/contracts_osmosis/auctions_manager.wasm b/examples/contracts_osmosis/auctions_manager.wasm new file mode 100644 index 0000000..2837aa7 Binary files /dev/null and b/examples/contracts_osmosis/auctions_manager.wasm differ diff --git a/examples/contracts_osmosis/cw1_whitelist.wasm b/examples/contracts_osmosis/cw1_whitelist.wasm new file mode 100644 index 0000000..c722e16 Binary files /dev/null and b/examples/contracts_osmosis/cw1_whitelist.wasm differ diff --git a/examples/contracts_osmosis/cw20_base.wasm b/examples/contracts_osmosis/cw20_base.wasm new file mode 100644 index 0000000..95d9f2a Binary files /dev/null and b/examples/contracts_osmosis/cw20_base.wasm differ diff --git a/examples/contracts_osmosis/price_oracle.wasm b/examples/contracts_osmosis/price_oracle.wasm new file mode 100644 index 0000000..fe14ead Binary files /dev/null and b/examples/contracts_osmosis/price_oracle.wasm differ diff --git a/examples/neutron.rs b/examples/neutron.rs index 2d301e7..ac99bff 100644 --- a/examples/neutron.rs +++ b/examples/neutron.rs @@ -9,8 +9,8 @@ const ARTIFACTS_DIR: &str = "contracts"; const ACC_0_ADDR: &str = "neutron1hj5fveer5cjtn4wd6wstzugjfdxzl0xpznmsky"; const LOCAL_CODE_ID_CACHE_PATH: &str = "code_id_cache.json"; -const TEST_TOKEN_1_NAME: &str = "bruhtoken3"; -const TEST_TOKEN_2_NAME: &str = "amoguscoin3"; +const TEST_TOKEN_1_NAME: &str = "bruhtoken"; +const TEST_TOKEN_2_NAME: &str = "amoguscoin"; /// Demonstrates using localic-utils for neutron. fn main() -> Result<(), Box> { @@ -39,8 +39,16 @@ fn main() -> Result<(), Box> { .with_subdenom(TEST_TOKEN_2_NAME) .send()?; - let bruhtoken = ctx.get_tokenfactory_denom(ACC_0_ADDR, TEST_TOKEN_1_NAME); - let amoguscoin = ctx.get_tokenfactory_denom(ACC_0_ADDR, TEST_TOKEN_2_NAME); + let bruhtoken = ctx + .get_tokenfactory_denom() + .creator(ACC_0_ADDR) + .subdenom(TEST_TOKEN_1_NAME.to_owned()) + .get(); + let amoguscoin = ctx + .get_tokenfactory_denom() + .creator(ACC_0_ADDR) + .subdenom(TEST_TOKEN_2_NAME.to_owned()) + .get(); // Deploy valence auctions ctx.build_tx_create_auctions_manager() @@ -81,14 +89,26 @@ fn main() -> Result<(), Box> { .with_amount_offer_asset(10000) .send()?; - ctx.get_auction(( - "untrn", - ctx.get_tokenfactory_denom(ACC_0_ADDR, TEST_TOKEN_1_NAME), - ))?; - ctx.get_auction(( - "untrn", - ctx.get_tokenfactory_denom(ACC_0_ADDR, TEST_TOKEN_2_NAME), - ))?; + let _ = ctx + .get_auction() + .offer_asset("untrn") + .ask_asset( + &ctx.get_tokenfactory_denom() + .creator(ACC_0_ADDR) + .subdenom(TEST_TOKEN_1_NAME.to_owned()) + .get(), + ) + .get_cw(); + let _ = ctx + .get_auction() + .offer_asset("untrn") + .ask_asset( + &ctx.get_tokenfactory_denom() + .creator(ACC_0_ADDR) + .subdenom(TEST_TOKEN_2_NAME.to_owned()) + .get(), + ) + .get_cw(); ctx.build_tx_create_token_registry() .with_owner(ACC_0_ADDR) @@ -105,10 +125,16 @@ fn main() -> Result<(), Box> { .with_denom_b(bruhtoken) .send()?; - let pool = ctx.get_astroport_pool( - "untrn", - ctx.get_tokenfactory_denom(ACC_0_ADDR, TEST_TOKEN_2_NAME), - )?; + let pool = ctx + .get_astro_pool() + .denoms( + "untrn".to_owned(), + ctx.get_tokenfactory_denom() + .creator(ACC_0_ADDR) + .subdenom(TEST_TOKEN_2_NAME.to_owned()) + .get(), + ) + .get_cw(); assert!(pool .query_value(&serde_json::json!({ @@ -140,8 +166,9 @@ fn main() -> Result<(), Box> { .send()?; let factory_contract_code_id = ctx - .get_contract("astroport_whitelist") - .unwrap() + .get_contract() + .contract("astroport_whitelist") + .get_cw() .code_id .unwrap(); @@ -165,7 +192,7 @@ fn main() -> Result<(), Box> { .salt_hex_encoded(hex::encode("examplesalt").as_str()) .get(); - let mut cw = ctx.get_contract("astroport_whitelist").unwrap(); + let mut cw = ctx.get_contract().contract("astroport_whitelist").get_cw(); cw.contract_addr = Some(addr); cw.execute( diff --git a/examples/neutron_osmosis.rs b/examples/neutron_osmosis.rs index 3c633a6..2a27436 100644 --- a/examples/neutron_osmosis.rs +++ b/examples/neutron_osmosis.rs @@ -32,7 +32,11 @@ fn main() -> Result<(), Box> { .with_chain_name("neutron") .with_subdenom("bruhtoken") .send()?; - let bruhtoken = ctx.get_tokenfactory_denom(NEUTRON_ACC_0_ADDR, "bruhtoken"); + let bruhtoken = ctx + .get_tokenfactory_denom() + .creator(NEUTRON_ACC_0_ADDR) + .subdenom("bruhtoken".into()) + .get(); ctx.build_tx_mint_tokenfactory_token() .with_chain_name("neutron") .with_amount(10000000000000000000) @@ -53,8 +57,18 @@ fn main() -> Result<(), Box> { .with_amount(1000000) .send()?; - let ibc_bruhtoken = ctx.get_ibc_denom(&bruhtoken, "neutron", "osmosis"); - let ibc_neutron = ctx.get_ibc_denom("untrn", "neutron", "osmosis"); + let ibc_bruhtoken = ctx + .get_ibc_denom() + .base_denom(bruhtoken.clone()) + .src("neutron") + .dest("osmosis") + .get(); + let ibc_neutron = ctx + .get_ibc_denom() + .base_denom("untrn".into()) + .src("neutron") + .dest("osmosis") + .get(); // Create an osmosis pool ctx.build_tx_create_osmo_pool() @@ -65,7 +79,10 @@ fn main() -> Result<(), Box> { .send()?; // Get its id - let pool_id = ctx.get_osmo_pool(&ibc_neutron, &ibc_bruhtoken)?; + let pool_id = ctx + .get_osmo_pool() + .denoms(ibc_neutron.clone(), ibc_bruhtoken.clone()) + .get_u64(); // Fund the pool ctx.build_tx_fund_osmo_pool() diff --git a/examples/osmosis.rs b/examples/osmosis.rs index edfe8cd..ff803e2 100644 --- a/examples/osmosis.rs +++ b/examples/osmosis.rs @@ -21,7 +21,11 @@ fn main() -> Result<(), Box> { .with_chain_name(OSMOSIS_CHAIN_NAME) .with_subdenom("bruhtoken") .send()?; - let bruhtoken = ctx.get_tokenfactory_denom(ACC_0_ADDR, "bruhtoken"); + let bruhtoken = ctx + .get_tokenfactory_denom() + .creator(ACC_0_ADDR) + .subdenom("bruhtoken".into()) + .get(); ctx.build_tx_mint_tokenfactory_token() .with_chain_name(OSMOSIS_CHAIN_NAME) .with_amount(10000000000000000000) @@ -38,7 +42,10 @@ fn main() -> Result<(), Box> { .send()?; // Get its id - let pool_id = ctx.get_osmo_pool("uosmo", &bruhtoken)?; + let pool_id = ctx + .get_osmo_pool() + .denoms("uosmo".into(), bruhtoken.clone()) + .get_u64(); // Fund the pool ctx.build_tx_fund_osmo_pool() diff --git a/src/utils/queries.rs b/src/utils/queries.rs index bae9aac..bc803e9 100644 --- a/src/utils/queries.rs +++ b/src/utils/queries.rs @@ -9,184 +9,440 @@ use super::{ 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, + test_context::{LocalChain, TestContext}, }; -use localic_std::modules::cosmwasm::CosmWasm; +use localic_std::{modules::cosmwasm::CosmWasm, transactions::ChainRequestBuilder}; use serde_json::Value; use std::{path::PathBuf, thread, time::Duration}; -impl TestContext { - /// Gets the event log of a transaction as a JSON object, - /// or returns an error if it does not exist. - pub fn guard_tx_errors(&self, chain_name: &str, hash: &str) -> Result<(), Error> { - if !self.unwrap_logs { - return Ok(()); +pub enum QueryType { + TransferChannel, + Connection, + CCVChannel, + IBCDenom, + AdminAddr, + NativeDenom, + ChainPrefix, + RequestBuilder, + BuiltContractAddress, + CodeInfo, + Contract, + AuctionsManager, + PriceOracle, + Auction, + TokenfactoryDenom, + Factory, + AstroPool, + OsmoPool, +} + +pub struct TestContextQuery<'a> { + context: &'a TestContext, + query_type: QueryType, + src_chain: Option, + dest_chain: Option, + contract_name: Option, + + // denoms in a pool + offer_asset: Option, + ask_asset: Option, + denoms: Option<(String, String)>, + + base_denom: Option, + subdenom: Option, + + // build-contract-address query args + creator_address: Option, + salt_hex_encoded: Option, +} + +impl<'a> TestContextQuery<'a> { + pub fn new(context: &'a TestContext, query_type: QueryType) -> Self { + Self { + context, + query_type, + src_chain: Some(NEUTRON_CHAIN_NAME.to_owned()), + dest_chain: None, + contract_name: None, + offer_asset: None, + ask_asset: None, + base_denom: None, + subdenom: None, + denoms: None, + creator_address: None, + salt_hex_encoded: None, } + } - let chain = self.get_chain(chain_name); - let mut logs = None; + pub fn src(mut self, src_chain: &str) -> Self { + self.src_chain = Some(src_chain.to_string()); + self + } - for _ in 0..TX_HASH_QUERY_RETRIES { - thread::sleep(Duration::from_secs(TX_HASH_QUERY_PAUSE_SEC)); + pub fn dest(mut self, dest_chain: &str) -> Self { + self.dest_chain = Some(dest_chain.to_string()); + self + } - let mut tx_res = chain.rb.query_tx_hash(hash); + pub fn contract(mut self, contract_name: &str) -> Self { + self.contract_name = Some(contract_name.to_string()); + self + } - if tx_res.get("raw_log").is_none() { - continue; - } + pub fn offer_asset(mut self, offer_asset: &str) -> Self { + self.offer_asset = Some(offer_asset.to_owned()); + self + } - logs = Some(tx_res["raw_log"].take()); + pub fn ask_asset(mut self, ask_asset: &str) -> Self { + self.ask_asset = Some(ask_asset.to_owned()); + self + } - break; + pub fn denoms(mut self, denom_a: String, denom_b: String) -> Self { + self.denoms = Some((denom_a, denom_b)); + self + } + + pub fn base_denom(mut self, base_denom: String) -> Self { + self.base_denom = Some(base_denom); + self + } + + pub fn subdenom(mut self, subdenom: String) -> Self { + self.subdenom = Some(subdenom); + self + } + + pub fn creator(mut self, creator_addr: &str) -> Self { + self.creator_address = Some(creator_addr.to_owned()); + self + } + + pub fn salt_hex_encoded(mut self, salt_hex_encoded: &str) -> Self { + self.salt_hex_encoded = Some(salt_hex_encoded.to_owned()); + self + } + + pub fn get(self) -> String { + // None cases explicitly enumerated here to ensure compilation-time + // checking of query inclusion in some get_x method + match self.query_type { + QueryType::TransferChannel => self.get_transfer_channel().map(ToOwned::to_owned), + QueryType::Connection => self.get_connection_id().map(ToOwned::to_owned), + QueryType::CCVChannel => self.get_ccv_channel().map(ToOwned::to_owned), + QueryType::IBCDenom => self.get_ibc_denom(), + QueryType::AdminAddr => self.get_admin_addr().map(ToOwned::to_owned), + QueryType::NativeDenom => self.get_native_denom().map(ToOwned::to_owned), + QueryType::ChainPrefix => self.get_chain_prefix().map(ToOwned::to_owned), + QueryType::BuiltContractAddress => self.get_built_contract_address(), + QueryType::TokenfactoryDenom => self.get_tokenfactory_denom(), + QueryType::CodeInfo + | QueryType::OsmoPool + | QueryType::AuctionsManager + | QueryType::AstroPool + | QueryType::Auction + | QueryType::Contract + | QueryType::Factory + | QueryType::RequestBuilder + | QueryType::PriceOracle => None, } + .unwrap() + } - let raw_log = logs.as_ref().and_then(|raw_log| raw_log.as_str()).unwrap(); + pub fn get_cw(self) -> CosmWasm<'a> { + match self.query_type { + QueryType::Contract => self.get_contract(), + QueryType::Auction => self.get_auction(), + QueryType::Factory | QueryType::PriceOracle | QueryType::AuctionsManager => { + self.get_deployed_contract() + } + QueryType::AstroPool => self.get_astro_pool(), + QueryType::TransferChannel + | QueryType::Connection + | QueryType::CCVChannel + | QueryType::IBCDenom + | QueryType::AdminAddr + | QueryType::NativeDenom + | QueryType::ChainPrefix + | QueryType::BuiltContractAddress + | QueryType::TokenfactoryDenom + | QueryType::OsmoPool + | QueryType::RequestBuilder + | QueryType::CodeInfo => None, + } + .unwrap() + } - if raw_log.is_empty() { - return Ok(()); + pub fn get_value(self) -> Value { + match self.query_type { + QueryType::CodeInfo => self.get_code_info(), + QueryType::TransferChannel + | QueryType::Connection + | QueryType::CCVChannel + | QueryType::IBCDenom + | QueryType::AdminAddr + | QueryType::NativeDenom + | QueryType::ChainPrefix + | QueryType::BuiltContractAddress + | QueryType::TokenfactoryDenom + | QueryType::OsmoPool + | QueryType::AuctionsManager + | QueryType::AstroPool + | QueryType::Auction + | QueryType::Contract + | QueryType::Factory + | QueryType::RequestBuilder + | QueryType::PriceOracle => None, } + .unwrap() + } - let logs = serde_json::from_str::(raw_log).map_err(|_| Error::TxFailed { - hash: hash.to_owned(), - error: raw_log.to_owned(), - })?; + pub fn get_u64(self) -> u64 { + match self.query_type { + QueryType::OsmoPool => self.get_osmo_pool(), + QueryType::TransferChannel + | QueryType::Connection + | QueryType::CCVChannel + | QueryType::IBCDenom + | QueryType::AdminAddr + | QueryType::NativeDenom + | QueryType::ChainPrefix + | QueryType::BuiltContractAddress + | QueryType::TokenfactoryDenom + | QueryType::AuctionsManager + | QueryType::AstroPool + | QueryType::Auction + | QueryType::Contract + | QueryType::Factory + | QueryType::RequestBuilder + | QueryType::PriceOracle + | QueryType::CodeInfo => None, + } + .unwrap() + } - if let Some(err) = logs.as_str() { - return Err(Error::TxFailed { - hash: hash.to_owned(), - error: err.to_owned(), - }); + pub fn get_all(self) -> Vec { + match self.query_type { + QueryType::TransferChannel => self.get_all_transfer_channels(), + QueryType::Connection => self.get_all_connections(), + _ => vec![], } + .into_iter() + .map(ToOwned::to_owned) + .collect::>() + } - Ok(()) + pub fn get_request_builder(mut self, chain: &str) -> &'a ChainRequestBuilder { + self.src_chain = Some(chain.to_string()); + let rb = match self.query_type { + QueryType::RequestBuilder => self.get_rb(), + _ => None, + }; + rb.unwrap() } - /// Get a new CosmWasm instance for a contract identified by a name. - pub fn get_contract(&self, name: &str) -> Result { - let chain = self.get_chain(NEUTRON_CHAIN_NAME); + fn get_transfer_channel(&self) -> Option<&str> { + self.context + .transfer_channel_ids + .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) + .map(|s| s.as_str()) + } - let code_id = chain - .contract_codes - .get(name) - .ok_or(Error::Misc(format!("contract '{name}' is missing")))?; + fn get_all_transfer_channels(&self) -> Vec<&str> { + self.src_chain + .as_ref() + .map(|src| { + self.context + .transfer_channel_ids + .iter() + .filter(|((s, _), _)| s == src) + .map(|(_, v)| v.as_str()) + .collect::>() + }) + .unwrap_or_default() + } - let artifacts_path = &self.artifacts_dir; + fn get_connection_id(&self) -> Option<&str> { + self.context + .connection_ids + .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) + .map(|s| s.as_str()) + } - Ok(CosmWasm::new_from_existing( - &chain.rb, - Some(PathBuf::from(format!("{artifacts_path}/{name}.wasm"))), - Some(*code_id), - None, - )) + fn get_all_connections(&self) -> Vec<&str> { + self.src_chain + .as_ref() + .map(|src| { + self.context + .connection_ids + .iter() + .filter(|((s, _), _)| s == src) + .map(|(_, s)| s.as_str()) + .collect::>() + }) + .unwrap_or_default() } - /// Get a new CosmWasm instance for the existing deployed auctions manager. - pub fn get_auctions_manager(&self) -> Result { - let neutron = self.get_chain(NEUTRON_CHAIN_NAME); + fn get_ccv_channel(&self) -> Option<&str> { + self.context + .ccv_channel_ids + .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) + .map(|s| s.as_str()) + } - let contract_addr = neutron - .contract_addrs - .get(AUCTIONS_MANAGER_CONTRACT_NAME) - .unwrap(); - let code_id = neutron - .contract_codes - .get(AUCTIONS_MANAGER_CONTRACT_NAME) - .unwrap(); + fn get_ibc_denom(&self) -> Option { + let dest_chain = self.dest_chain.as_deref()?; + let src_chain = self.src_chain.as_deref()?; - Ok(CosmWasm::new_from_existing( - &neutron.rb, - None, - Some(*code_id), - Some(contract_addr.clone()), - )) + let channel_id = self + .context + .get_transfer_channels() + .src(dest_chain) + .dest(src_chain) + .get(); + + let prefixed_denom = get_prefixed_denom( + TRANSFER_PORT.to_string(), + channel_id.to_string(), + self.base_denom.clone()?, + ); + + let src_denom_trace = parse_denom_trace(prefixed_denom); + let ibc_denom = src_denom_trace.ibc_denom(); + + Some(ibc_denom) } - /// Get a new CosmWasm instance for the existing deployed auctions manager. - pub fn get_price_oracle(&self) -> Result { - let neutron = self.get_chain(NEUTRON_CHAIN_NAME); + fn get_admin_addr(&self) -> Option<&str> { + let src = self.src_chain.as_deref()?; + + Some(self.context.chains.get(src)?.admin_addr.as_ref()) + } - let mut contract = self.get_contract(PRICE_ORACLE_NAME)?; - let contract_addr = - neutron - .contract_addrs - .get(PRICE_ORACLE_NAME) - .ok_or(Error::MissingContextVariable(String::from( - "contract_addrs::price_oracle", - )))?; - contract.contract_addr = Some(contract_addr.clone()); + fn get_native_denom(&self) -> Option<&str> { + let src = self.src_chain.as_deref()?; - Ok(contract) + Some(self.context.chains.get(src)?.native_denom.as_ref()) } - /// Gets a CosmWasm instance for an auction with a given pair. - pub fn get_auction, TDenomB: AsRef>( - &self, - denoms: (TDenomA, TDenomB), - ) -> Result { - let mut auction_contract = self.get_contract(AUCTION_CONTRACT_NAME)?; + fn get_chain_prefix(&self) -> Option<&str> { + let src = self.src_chain.as_deref()?; - // The auctions manager for this deployment - let contract_a = self.get_auctions_manager()?; + Some(self.context.chains.get(src)?.chain_prefix.as_ref()) + } - // Get the address of the auction specified - let resp = contract_a.query_value(&serde_json::json!({ - "get_pair_addr": { - "pair": (denoms.0.as_ref(), denoms.1.as_ref()) - } - })); + fn get_code_info(&self) -> Option { + let contract = self + .context + .get_contract() + .src(self.src_chain.as_deref()?) + .contract(self.contract_name.as_ref()?) + .get_cw(); + let code_id = contract.code_id?; + let chain = self.context.chains.get(self.src_chain.as_deref()?)?; + + // This will produce a { ... text: "{ 'data_hash': xyz }" }. Get the code info enclosed + let resp = chain.rb.query(&format!("q wasm code-info {code_id}"), true); + + let str_info_object = resp["text"].as_str()?; + serde_json::from_str(str_info_object).ok() + } - auction_contract.contract_addr = Some( - resp.get("data") - .and_then(|json| json.as_str()) - .ok_or(Error::Misc(format!("tx failed with resp: {:?}", resp)))? - .to_owned(), - ); + fn get_tokenfactory_denom(&self) -> Option { + let creator_addr = self.creator_address.as_deref()?; + let subdenom = self.subdenom.as_deref()?; - Ok(auction_contract) + Some(format!("factory/{creator_addr}/{subdenom}")) } - pub fn get_tokenfactory_denom(&self, creator_addr: &str, subdenom: &str) -> String { - format!("factory/{creator_addr}/{subdenom}") + fn get_built_contract_address(&self) -> Option { + let code_info = self.get_code_info()?; + let code_id_hash = code_info["data_hash"].as_str()?; + + let creator_address = self.creator_address.as_ref()?; + let salt = self.salt_hex_encoded.as_deref()?; + + let chain = self.context.chains.get(self.src_chain.as_deref()?)?; + + let mut resp = chain.rb.bin( + &format!("q wasm build-address {code_id_hash} {creator_address} {salt}"), + true, + ); + + // text field contains built address + match resp["text"].take() { + Value::String(s) => Some(s.replace("\n", "")), + _ => None, + } } - /// Gets the deployed atroport factory for Neutron. - pub fn get_astroport_factory(&self) -> Result { - let neutron = self.get_chain(NEUTRON_CHAIN_NAME); + fn get_contract(&self) -> Option> { + let chain: &LocalChain = self.context.get_chain(self.src_chain.as_deref()?); + let name = self.contract_name.as_deref()?; - let code_id = - neutron - .contract_codes - .get(FACTORY_NAME) - .ok_or(Error::MissingContextVariable(format!( - "contract_codes::{FACTORY_NAME}", - )))?; - let contract_addr = - neutron - .contract_addrs - .get(FACTORY_NAME) - .ok_or(Error::MissingContextVariable(format!( - "contract_addrs::{FACTORY_NAME}", - )))?; + let code_id = chain.contract_codes.get(name)?; + let artifacts_path = &self.context.artifacts_dir; - let artifacts_path = self.artifacts_dir.as_str(); + Some(CosmWasm::new_from_existing( + &chain.rb, + Some(PathBuf::from(format!("{artifacts_path}/{name}.wasm"))), + Some(*code_id), + None, + )) + } + + fn get_deployed_contract(&self) -> Option> { + let chain = self.context.get_chain(self.src_chain.as_deref()?); + let name = self.contract_name.as_deref()?; - Ok(CosmWasm::new_from_existing( - &neutron.rb, - Some(PathBuf::from(format!( - "{artifacts_path}/{FACTORY_NAME}.wasm" - ))), + let code_id = chain.contract_codes.get(name)?; + let contract_addr = chain.contract_addrs.get(name)?.clone(); + let artifacts_path = &self.context.artifacts_dir; + + Some(CosmWasm::new_from_existing( + &chain.rb, + Some(PathBuf::from(format!("{artifacts_path}/{name}.wasm"))), Some(*code_id), - Some(contract_addr.clone()), + Some(contract_addr), )) } - /// Gets a previously deployed astroport pair. - pub fn get_astroport_pool( - &self, - denom_a: impl AsRef, - denom_b: impl AsRef, - ) -> Result { - let factory = self.get_astroport_factory()?; + fn get_auction(&self) -> Option> { + let auction_manager = self + .context + .get_auctions_manager() + .src(self.src_chain.as_deref()?) + .get_cw(); + let denoms = (self.offer_asset.as_deref()?, self.ask_asset.as_deref()?); + + let resp = auction_manager.query( + &serde_json::to_string(&serde_json::json!({ + "get_pair_addr": { + "pair": denoms, + }} + )) + .unwrap(), + ); + + let mut cw = self + .context + .get_contract() + .contract(PAIR_NAME) + .src(self.src_chain.as_deref()?) + .get_cw(); + cw.contract_addr = Some(resp["data"].as_str()?.to_owned()); + + Some(cw) + } + + fn get_astro_pool(&self) -> Option> { + let (denom_a, denom_b) = self.denoms.as_ref()?; + let factory = self + .context + .get_factory() + .src(self.src_chain.as_deref()?) + .get_cw(); let pair_info = factory.query_value(&serde_json::json!( { @@ -194,12 +450,12 @@ impl TestContext { "asset_infos": [ { "native_token": { - "denom": denom_a.as_ref(), + "denom": denom_a, } }, { "native_token": { - "denom": denom_b.as_ref(), + "denom": denom_b, } } ] @@ -217,45 +473,51 @@ impl TestContext { .and_then(|data| data.get("pair_type")) .unwrap(); - let neutron = self.get_chain(NEUTRON_CHAIN_NAME); + let chain = self.context.get_chain(self.src_chain.as_deref()?); if kind.get("xyk").is_some() { - let contract = self.get_contract(PAIR_NAME)?; - - return Ok(CosmWasm::new_from_existing( - &neutron.rb, + let contract = self + .context + .get_contract() + .contract(PAIR_NAME) + .src(self.src_chain.as_deref()?) + .get_cw(); + + return Some(CosmWasm::new_from_existing( + &chain.rb, contract.file_path, contract.code_id, Some(addr.to_owned()), )); } - let contract = self.get_contract(STABLE_PAIR_NAME)?; + let contract = self + .context + .get_contract() + .contract(STABLE_PAIR_NAME) + .src(self.src_chain.as_deref()?) + .get_cw(); - Ok(CosmWasm::new_from_existing( - &neutron.rb, + Some(CosmWasm::new_from_existing( + &chain.rb, contract.file_path, contract.code_id, Some(addr.to_owned()), )) } - /// Gets the id of the pool with the specifieed denoms. - pub fn get_osmo_pool( - &self, - denom_a: impl AsRef, - denom_b: impl AsRef, - ) -> Result { - let osmosis = self.get_chain(OSMOSIS_CHAIN_NAME); - let denom_a_str = denom_a.as_ref(); + fn get_osmo_pool(&self) -> Option { + // Do not use get_chain here, since we only want to support osmosis pools on osmosis + let (denom_a, denom_b) = self.denoms.as_ref()?; + let osmosis = self.context.get_chain(OSMOSIS_CHAIN_NAME); let res = osmosis.rb.query( - &format!("q poolmanager list-pools-by-denom {denom_a_str} --output=json"), + &format!("q poolmanager list-pools-by-denom {denom_a} --output=json"), true, ); let res_text = res.get("text").and_then(|v| v.as_str()).unwrap(); - let res_value: Value = serde_json::from_str(res_text)?; + let res_value: Value = serde_json::from_str(res_text).ok()?; let pools_value = res_value.get("pools").unwrap(); let pool = pools_value @@ -278,42 +540,142 @@ impl TestContext { .and_then(|id_str| id_str.as_str()) .unwrap(); - Ok(pool.parse().unwrap()) + Some(pool.parse().unwrap()) } - /// Gets the IBC denom for a base denom given a src and dest chain. - 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.to_string(), dest_chain.to_string())) - { - return denom.clone(); + fn get_rb(&self) -> Option<&'a ChainRequestBuilder> { + if let Some(ref src) = self.src_chain { + self.context.chains.get(src).map(|chain| &chain.rb) + } else { + None } + } +} - let channel_id = self - .get_transfer_channels() - .src(dest_chain) - .dest(src_chain) - .get(); +impl TestContext { + pub fn get_transfer_channels(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::TransferChannel) + } - let prefixed_denom = get_prefixed_denom( - TRANSFER_PORT.to_string(), - channel_id.to_string(), - base_denom.to_string(), - ); + pub fn get_connections(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::Connection) + } - let src_denom_trace = parse_denom_trace(prefixed_denom); - let ibc_denom = src_denom_trace.ibc_denom(); + pub fn get_ccv_channels(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::CCVChannel) + } - self.ibc_denoms.insert( - (base_denom.to_string(), dest_chain.to_string()), - ibc_denom.clone(), - ); - self.ibc_denoms.insert( - (ibc_denom.clone(), src_chain.to_string()), - base_denom.to_string(), - ); + pub fn get_ibc_denom(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::IBCDenom) + } + + pub fn get_admin_addr(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::AdminAddr) + } + + pub fn get_native_denom(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::NativeDenom) + } + + pub fn get_chain_prefix(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::ChainPrefix) + } + + pub fn get_request_builder(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::RequestBuilder) + } + + pub fn get_code_info(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::CodeInfo) + } + + pub fn get_built_contract_address(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::BuiltContractAddress) + } + + pub fn get_contract(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::Contract) + } + + pub fn get_tokenfactory_denom(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::TokenfactoryDenom) + } + + pub fn get_price_oracle(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::PriceOracle).contract(PRICE_ORACLE_NAME) + } + + pub fn get_auction(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::Auction).contract(AUCTION_CONTRACT_NAME) + } + + pub fn get_factory(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::Factory).contract(FACTORY_NAME) + } - ibc_denom + pub fn get_astro_pool(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::AstroPool) + } + + pub fn get_osmo_pool(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::OsmoPool) + } + + pub fn get_auctions_manager(&self) -> TestContextQuery { + TestContextQuery::new(self, QueryType::AuctionsManager) + .contract(AUCTIONS_MANAGER_CONTRACT_NAME) + } + + pub fn get_chain(&self, chain_name: &str) -> &LocalChain { + self.chains.get(chain_name).unwrap() + } + + pub fn get_mut_chain(&mut self, chain_name: &str) -> &mut LocalChain { + self.chains.get_mut(chain_name).unwrap() + } + + /// Gets the event log of a transaction as a JSON object, + /// or returns an error if it does not exist. + pub fn guard_tx_errors(&self, chain_name: &str, hash: &str) -> Result<(), Error> { + if !self.unwrap_logs { + return Ok(()); + } + + let chain = self.get_chain(chain_name); + let mut logs = None; + + for _ in 0..TX_HASH_QUERY_RETRIES { + thread::sleep(Duration::from_secs(TX_HASH_QUERY_PAUSE_SEC)); + + let mut tx_res = chain.rb.query_tx_hash(hash); + + if tx_res.get("raw_log").is_none() { + continue; + } + + logs = Some(tx_res["raw_log"].take()); + + break; + } + + let raw_log = logs.as_ref().and_then(|raw_log| raw_log.as_str()).unwrap(); + + if raw_log.is_empty() { + return Ok(()); + } + + let logs = serde_json::from_str::(raw_log).map_err(|_| Error::TxFailed { + hash: hash.to_owned(), + error: raw_log.to_owned(), + })?; + + if let Some(err) = logs.as_str() { + return Err(Error::TxFailed { + hash: hash.to_owned(), + error: err.to_owned(), + }); + } + + Ok(()) } } diff --git a/src/utils/setup/astroport.rs b/src/utils/setup/astroport.rs index 7558227..c72f374 100644 --- a/src/utils/setup/astroport.rs +++ b/src/utils/setup/astroport.rs @@ -216,7 +216,11 @@ impl TestContext { key: &str, owner_addr: impl Into, ) -> Result<(), Error> { - let mut contract_a = self.get_contract(TOKEN_REGISTRY_NAME)?; + let mut contract_a = self + .get_contract() + .src(NEUTRON_CHAIN_NAME) + .contract(TOKEN_REGISTRY_NAME) + .get_cw(); let contract = contract_a.instantiate( key, @@ -291,7 +295,11 @@ impl TestContext { )), )?; - let mut contract_a = self.get_contract(FACTORY_NAME)?; + let mut contract_a = self + .get_contract() + .src(NEUTRON_CHAIN_NAME) + .contract(FACTORY_NAME) + .get_cw(); let contract = contract_a.instantiate( key, @@ -359,7 +367,7 @@ impl TestContext { denom_b: impl Into, ) -> Result<(), Error> { // Factory contract instance - let contract_a = self.get_astroport_factory()?; + let contract_a = self.get_factory().src(NEUTRON_CHAIN_NAME).get_cw(); // Create the pair let tx = contract_a.execute( @@ -409,18 +417,19 @@ impl TestContext { fn tx_fund_pool( &mut self, key: &str, - denom_a: impl Into + AsRef, - denom_b: impl Into + AsRef, + denom_a: String, + denom_b: String, amt_denom_a: u128, amt_denom_b: u128, slippage_tolerance: Decimal, liq_token_receiver: impl Into, ) -> Result<(), Error> { // Get the instance from the address - let pool = self.get_astroport_pool(denom_a.as_ref(), denom_b.as_ref())?; - - let denom_a = denom_a.into(); - let denom_b = denom_b.into(); + let pool = self + .get_astro_pool() + .src(NEUTRON_CHAIN_NAME) + .denoms(denom_a.clone(), denom_b.clone()) + .get_cw(); // Provide liquidity let tx = pool diff --git a/src/utils/setup/stride.rs b/src/utils/setup/stride.rs index b9f2d45..c107a00 100644 --- a/src/utils/setup/stride.rs +++ b/src/utils/setup/stride.rs @@ -58,7 +58,12 @@ impl TestContext { pub fn set_up_stride_host_zone(&mut self, dest_chain: &str) { let native_denom = self.get_native_denom().src(dest_chain).get().clone(); - let host_denom_on_stride = self.get_ibc_denom(&native_denom, dest_chain, STRIDE_CHAIN_NAME); + let host_denom_on_stride = self + .get_ibc_denom() + .base_denom(native_denom) + .dest(dest_chain) + .src(STRIDE_CHAIN_NAME) + .get(); let stride = self.get_chain(STRIDE_CHAIN_NAME); let stride_rb = &stride.rb; diff --git a/src/utils/setup/valence.rs b/src/utils/setup/valence.rs index f93a1f4..8833bb8 100644 --- a/src/utils/setup/valence.rs +++ b/src/utils/setup/valence.rs @@ -395,7 +395,10 @@ impl TestContext { min_auction_amount: impl AsRef<[(&'a str, MinAmount)]>, server_addr: impl AsRef, ) -> Result<(), Error> { - let mut contract_a: CosmWasm = self.get_contract(AUCTIONS_MANAGER_CONTRACT_NAME)?; + let mut contract_a: CosmWasm = self + .get_contract() + .contract(AUCTIONS_MANAGER_CONTRACT_NAME) + .get_cw(); let neutron = self.get_chain(NEUTRON_CHAIN_NAME); let auction_code_id = @@ -446,7 +449,7 @@ impl TestContext { seconds_allow_manual_change: u64, seconds_auction_prices_fresh: u64, ) -> Result<(), Error> { - let auctions_manager: CosmWasm = self.get_auctions_manager()?; + let auctions_manager: CosmWasm = self.get_auctions_manager().get_cw(); let auctions_manager_addr = auctions_manager .contract_addr @@ -454,7 +457,7 @@ impl TestContext { "contract_addresses::auctions_manager", )))?; - let mut contract_a = self.get_contract(PRICE_ORACLE_NAME)?; + let mut contract_a = self.get_contract().contract(PRICE_ORACLE_NAME).get_cw(); let contract = contract_a.instantiate( sender_key, serde_json::json!({ @@ -515,7 +518,7 @@ impl TestContext { amount_denom_a: u128, ) -> Result<(), Error> { // The auctions manager for this deployment - let contract_a = self.get_auctions_manager()?; + let contract_a = self.get_auctions_manager().get_cw(); let denom_a = pair.0.as_ref(); let receipt = contract_a.execute( @@ -570,10 +573,15 @@ impl TestContext { pair: (TDenomA, TDenomB), ) -> Result<(), Error> { // The auctions manager for this deployment - let contract_a = self.get_auctions_manager()?; - let code_id = self.get_contract(AUCTION_CONTRACT_NAME)?.code_id.ok_or( - Error::MissingContextVariable(String::from("code_ids::auction")), - )?; + let contract_a = self.get_auctions_manager().get_cw(); + let code_id = self + .get_contract() + .contract(AUCTION_CONTRACT_NAME) + .get_cw() + .code_id + .ok_or(Error::MissingContextVariable(String::from( + "code_ids::auction", + )))?; let receipt = contract_a.execute( sender_key, @@ -618,7 +626,7 @@ impl TestContext { fn tx_update_auction_oracle(&mut self, sender_key: &str) -> Result<(), Error> { // The auctions manager for this deployment - let contract_a = self.get_auctions_manager()?; + let contract_a = self.get_auctions_manager().get_cw(); let neutron = self.get_chain(NEUTRON_CHAIN_NAME); let oracle = neutron @@ -669,7 +677,7 @@ impl TestContext { price: Decimal, ) -> Result<(), Error> { // The auctions manager for this deployment - let oracle = self.get_price_oracle()?; + let oracle = self.get_price_oracle().get_cw(); let receipt = oracle.execute( sender_key, @@ -711,7 +719,7 @@ impl TestContext { pair: (TDenomA, TDenomB), amt_offer_asset: u128, ) -> Result<(), Error> { - let manager = self.get_auctions_manager()?; + let manager = self.get_auctions_manager().get_cw(); let denom_a = pair.0.as_ref(); @@ -753,7 +761,7 @@ impl TestContext { end_blocks: u128, pair: (TDenomA, TDenomB), ) -> Result<(), Error> { - let manager = self.get_auctions_manager()?; + let manager = self.get_auctions_manager().get_cw(); let neutron = self.get_chain(NEUTRON_CHAIN_NAME); let start_block_resp = neutron diff --git a/src/utils/test_context.rs b/src/utils/test_context.rs index 4637ab7..52cbbc1 100644 --- a/src/utils/test_context.rs +++ b/src/utils/test_context.rs @@ -4,7 +4,7 @@ use super::super::{ config::{ConfigChain, Logs}, ibc::Channel as QueryChannel, }, - ICTEST_HOME_VAR, LOCAL_IC_API_URL, NEUTRON_CHAIN_NAME, TRANSFER_PORT, + ICTEST_HOME_VAR, LOCAL_IC_API_URL, TRANSFER_PORT, }; use localic_std::{ @@ -13,7 +13,6 @@ use localic_std::{ relayer::{Channel, Relayer}, transactions::ChainRequestBuilder, }; -use serde_json::Value; use std::{collections::HashMap, env, fs::OpenOptions, path::PathBuf}; /// A configurable builder that can be used to create a TestContext. @@ -438,276 +437,6 @@ impl LocalChain { } } -impl TestContext { - pub fn get_transfer_channels(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::TransferChannel) - } - - pub fn get_connections(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::Connection) - } - - pub fn get_ccv_channels(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::CCVChannel) - } - - pub fn get_ibc_denoms(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::IBCDenom) - } - - pub fn get_admin_addr(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::AdminAddr) - } - - pub fn get_native_denom(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::NativeDenom) - } - - pub fn get_chain_prefix(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::ChainPrefix) - } - - pub fn get_request_builder(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::RequestBuilder) - } - - pub fn get_built_contract_address(&self) -> TestContextQuery { - TestContextQuery::new(self, QueryType::BuiltContractAddress) - } - - pub fn get_chain(&self, chain_name: &str) -> &LocalChain { - self.chains.get(chain_name).unwrap() - } - - pub fn get_mut_chain(&mut self, chain_name: &str) -> &mut LocalChain { - self.chains.get_mut(chain_name).unwrap() - } -} - -pub enum QueryType { - TransferChannel, - Connection, - CCVChannel, - IBCDenom, - AdminAddr, - NativeDenom, - ChainPrefix, - RequestBuilder, - BuiltContractAddress, - CodeInfo, -} - -pub struct TestContextQuery<'a> { - context: &'a TestContext, - query_type: QueryType, - src_chain: Option, - dest_chain: Option, - contract_name: Option, - - // build-contract-address query args - creator_address: Option, - salt_hex_encoded: Option, -} - -impl<'a> TestContextQuery<'a> { - pub fn new(context: &'a TestContext, query_type: QueryType) -> Self { - Self { - context, - query_type, - src_chain: None, - dest_chain: None, - contract_name: None, - creator_address: None, - salt_hex_encoded: None, - } - } - - pub fn src(mut self, src_chain: &str) -> Self { - self.src_chain = Some(src_chain.to_string()); - self - } - - pub fn dest(mut self, dest_chain: &str) -> Self { - self.dest_chain = Some(dest_chain.to_string()); - self - } - - pub fn contract(mut self, contract_name: &str) -> Self { - self.contract_name = Some(contract_name.to_string()); - self - } - - pub fn creator(mut self, creator_addr: &str) -> Self { - self.creator_address = Some(creator_addr.to_owned()); - self - } - - pub fn salt_hex_encoded(mut self, salt_hex_encoded: &str) -> Self { - self.salt_hex_encoded = Some(salt_hex_encoded.to_owned()); - self - } - - pub fn get(self) -> String { - match self.query_type { - QueryType::TransferChannel => self.get_transfer_channel().map(ToOwned::to_owned), - QueryType::Connection => self.get_connection_id().map(ToOwned::to_owned), - QueryType::CCVChannel => self.get_ccv_channel().map(ToOwned::to_owned), - QueryType::IBCDenom => self.get_ibc_denom().map(ToOwned::to_owned), - QueryType::AdminAddr => self.get_admin_addr().map(ToOwned::to_owned), - QueryType::NativeDenom => self.get_native_denom().map(ToOwned::to_owned), - QueryType::ChainPrefix => self.get_chain_prefix().map(ToOwned::to_owned), - QueryType::BuiltContractAddress => self.get_built_contract_address(), - QueryType::CodeInfo => self.get_code_info().map(|s| s.to_string()), - _ => None, - } - .unwrap() - } - - pub fn get_all(self) -> Vec { - match self.query_type { - QueryType::TransferChannel => self.get_all_transfer_channels(), - QueryType::Connection => self.get_all_connections(), - _ => vec![], - } - .into_iter() - .map(ToOwned::to_owned) - .collect::>() - } - - pub fn get_request_builder(mut self, chain: &str) -> &'a ChainRequestBuilder { - self.src_chain = Some(chain.to_string()); - let rb = match self.query_type { - QueryType::RequestBuilder => self.get_rb(), - _ => None, - }; - rb.unwrap() - } - - fn get_transfer_channel(&self) -> Option<&str> { - self.context - .transfer_channel_ids - .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) - .map(|s| s.as_str()) - } - - fn get_all_transfer_channels(&self) -> Vec<&str> { - self.src_chain - .as_ref() - .map(|src| { - self.context - .transfer_channel_ids - .iter() - .filter(|((s, _), _)| s == src) - .map(|(_, v)| v.as_str()) - .collect::>() - }) - .unwrap_or_default() - } - - fn get_connection_id(&self) -> Option<&str> { - self.context - .connection_ids - .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) - .map(|s| s.as_str()) - } - - fn get_all_connections(&self) -> Vec<&str> { - self.src_chain - .as_ref() - .map(|src| { - self.context - .connection_ids - .iter() - .filter(|((s, _), _)| s == src) - .map(|(_, s)| s.as_str()) - .collect::>() - }) - .unwrap_or_default() - } - - fn get_ccv_channel(&self) -> Option<&str> { - self.context - .ccv_channel_ids - .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) - .map(|s| s.as_str()) - } - - fn get_ibc_denom(&self) -> Option<&str> { - self.context - .ibc_denoms - .get(&(self.src_chain.clone()?, self.dest_chain.clone()?)) - .map(|s| s.as_str()) - } - - fn get_admin_addr(&self) -> Option<&str> { - let src = self.src_chain.as_deref()?; - - Some(self.context.chains.get(src)?.admin_addr.as_ref()) - } - - fn get_native_denom(&self) -> Option<&str> { - let src = self.src_chain.as_deref()?; - - Some(self.context.chains.get(src)?.native_denom.as_ref()) - } - - fn get_chain_prefix(&self) -> Option<&str> { - let src = self.src_chain.as_deref()?; - - Some(self.context.chains.get(src)?.chain_prefix.as_ref()) - } - - fn get_code_info(&self) -> Option { - let contract = self - .context - .get_contract(self.contract_name.as_ref()?) - .ok()?; - let code_id = contract.code_id?; - let chain = self - .context - .chains - .get(self.src_chain.as_deref().unwrap_or(NEUTRON_CHAIN_NAME))?; - - // This will produce a { ... text: "{ 'data_hash': xyz }" }. Get the code info enclosed - let resp = chain.rb.query(&format!("q wasm code-info {code_id}"), true); - - let str_info_object = resp["text"].as_str()?; - serde_json::from_str(str_info_object).ok() - } - - fn get_built_contract_address(&self) -> Option { - let code_info = self.get_code_info()?; - let code_id_hash = code_info["data_hash"].as_str()?; - - let creator_address = self.creator_address.as_ref()?; - let salt = self.salt_hex_encoded.as_deref()?; - - let chain = self - .context - .chains - .get(self.src_chain.as_deref().unwrap_or(NEUTRON_CHAIN_NAME))?; - - let mut resp = chain.rb.bin( - &format!("q wasm build-address {code_id_hash} {creator_address} {salt}"), - true, - ); - - // text field contains built address - match resp["text"].take() { - Value::String(s) => Some(s.replace("\n", "")), - _ => None, - } - } - - fn get_rb(&self) -> Option<&'a ChainRequestBuilder> { - if let Some(ref src) = self.src_chain { - self.context.chains.get(src).map(|chain| &chain.rb) - } else { - None - } - } -} - pub fn find_pairwise_transfer_channel_ids( rb: &ChainRequestBuilder, src_chain_id: &str,