From 0134361a37b95ab44ad7dea12065e37c1107bc79 Mon Sep 17 00:00:00 2001 From: Dowland Aiello Date: Fri, 16 Aug 2024 20:22:45 +0000 Subject: [PATCH] test_context: Implement a query for getting instantiate2 addrs. --- README.md | 2 +- examples/neutron.rs | 23 ++- src/utils/mod.rs | 2 +- src/utils/{fixtures.rs => queries.rs} | 0 src/utils/setup/wasm.rs | 2 +- src/utils/test_context.rs | 233 +++++++++++++++----------- 6 files changed, 161 insertions(+), 101 deletions(-) rename src/utils/{fixtures.rs => queries.rs} (100%) diff --git a/README.md b/README.md index ec01b54..5f32368 100644 --- a/README.md +++ b/README.md @@ -102,7 +102,7 @@ Note that most `tx_*` helper functions expose a `.with_key(key: &str)` builder f * `.build_tx_instantiate2` - Predictably instantiates a CosmWasm contract. * Required builder calls: * `.with_code_id(code_id: u64)` - Should be the raw code ID of the contract being instantiated - * `.with_salt(salt: &str)` - Should be a **hex-encoded** salt for instantiation + * `.with_salt_hex_encoded(salt: &str)` - Should be a **hex-encoded** salt for instantiation * `.with_msg(msg: serde_json::Value)` * `.with_label(label: &str)` * Notable optional builder calls: diff --git a/examples/neutron.rs b/examples/neutron.rs index fa9a118..81799b3 100644 --- a/examples/neutron.rs +++ b/examples/neutron.rs @@ -1,5 +1,7 @@ use cosmwasm_std::Decimal; -use localic_utils::{types::contract::MinAmount, ConfigChainBuilder, TestContextBuilder}; +use localic_utils::{ + types::contract::MinAmount, ConfigChainBuilder, TestContextBuilder, DEFAULT_KEY, +}; use std::error::Error; const ACC_0_ADDR: &str = "neutron1hj5fveer5cjtn4wd6wstzugjfdxzl0xpznmsky"; @@ -137,10 +139,27 @@ fn main() -> Result<(), Box> { "admins": [], "mutable": false, })) - .with_salt(hex::encode("examplesalt").as_str()) + .with_salt_hex_encoded(hex::encode("examplesalt").as_str()) .with_label("test_contract") .send() .unwrap(); + let addr = ctx + .get_built_contract_address() + .contract("astroport_whitelist") + .creator(ACC_0_ADDR) + .salt_hex_encoded(hex::encode("examplesalt").as_str()) + .get(); + + let mut cw = ctx.get_contract("astroport_whitelist").unwrap(); + cw.contract_addr = Some(addr); + + cw.execute( + DEFAULT_KEY, + &serde_json::json!({ "execute": { "msgs": [] } }).to_string(), + "", + ) + .unwrap(); + Ok(()) } diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 02786a1..fb6ae13 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -1,4 +1,4 @@ -mod fixtures; pub mod fs; +pub mod queries; pub mod setup; pub mod test_context; diff --git a/src/utils/fixtures.rs b/src/utils/queries.rs similarity index 100% rename from src/utils/fixtures.rs rename to src/utils/queries.rs diff --git a/src/utils/setup/wasm.rs b/src/utils/setup/wasm.rs index d1dc134..fb776e7 100644 --- a/src/utils/setup/wasm.rs +++ b/src/utils/setup/wasm.rs @@ -65,7 +65,7 @@ impl<'a> Instantiate2TxBuilder<'a> { } /// Sets the salt. Value must be hex encoded. - pub fn with_salt(&mut self, salt: &'a str) -> &mut Self { + pub fn with_salt_hex_encoded(&mut self, salt: &'a str) -> &mut Self { self.salt = Some(salt); self diff --git a/src/utils/test_context.rs b/src/utils/test_context.rs index bdbae81..c0feddb 100644 --- a/src/utils/test_context.rs +++ b/src/utils/test_context.rs @@ -1,13 +1,14 @@ use super::super::{ error::Error, types::{config::ConfigChain, contract::DeployedContractInfo, ibc::Channel as QueryChannel}, - LOCAL_IC_API_URL, TRANSFER_PORT, + LOCAL_IC_API_URL, NEUTRON_CHAIN_ADMIN_ADDR, NEUTRON_CHAIN_NAME, TRANSFER_PORT, }; use localic_std::{ modules::cosmwasm::CosmWasm, relayer::Channel, relayer::Relayer, transactions::ChainRequestBuilder, }; +use serde_json::Value; use std::{collections::HashMap, path::PathBuf}; /// A configurable builder that can be used to create a TestContext. @@ -435,6 +436,10 @@ impl TestContext { 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() } @@ -453,6 +458,8 @@ pub enum QueryType { NativeDenom, ChainPrefix, RequestBuilder, + BuiltContractAddress, + CodeInfo, } pub struct TestContextQuery<'a> { @@ -461,6 +468,10 @@ pub struct TestContextQuery<'a> { 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> { @@ -471,6 +482,8 @@ impl<'a> TestContextQuery<'a> { src_chain: None, dest_chain: None, contract_name: None, + creator_address: None, + salt_hex_encoded: None, } } @@ -489,18 +502,30 @@ impl<'a> TestContextQuery<'a> { 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 { - let query_response = match self.query_type { - QueryType::TransferChannel => self.get_transfer_channel(), - QueryType::Connection => self.get_connection_id(), - QueryType::CCVChannel => self.get_ccv_channel(), - QueryType::IBCDenom => self.get_ibc_denom(), - QueryType::AdminAddr => self.get_admin_addr(), - QueryType::NativeDenom => self.get_native_denom(), - QueryType::ChainPrefix => self.get_chain_prefix(), + 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, - }; - query_response.unwrap() + } + .unwrap() } pub fn get_all(self) -> Vec { @@ -509,6 +534,9 @@ impl<'a> TestContextQuery<'a> { 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 { @@ -520,110 +548,123 @@ impl<'a> TestContextQuery<'a> { rb.unwrap() } - fn get_transfer_channel(self) -> Option { - if let (Some(ref src), Some(ref dest)) = (self.src_chain, self.dest_chain) { - self.context - .transfer_channel_ids - .get(&(src.clone(), dest.clone())) - .cloned() - } else { - None - } + 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 { - if let Some(ref src) = self.src_chain { - self.context - .transfer_channel_ids - .iter() - .filter(|((s, _), _)| s == src) - .map(|(_, v)| v.clone()) - .collect::>() - } else { - vec![] - } + 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_connection_id(self) -> Option { - if let (Some(ref src), Some(ref dest)) = (self.src_chain, self.dest_chain) { - self.context - .connection_ids - .get(&(src.clone(), dest.clone())) - .cloned() - } else { - None - } + 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_all_connections(self) -> Vec { - if let Some(ref src) = self.src_chain { - self.context - .connection_ids - .iter() - .filter(|((s, _), _)| s == src) - .map(|(_, v)| v.clone()) - .collect::>() - } else { - vec![] - } + 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_ccv_channel(self) -> Option { - if let (Some(ref src), Some(ref dest)) = (self.src_chain, self.dest_chain) { - self.context - .ccv_channel_ids - .get(&(src.clone(), dest.clone())) - .cloned() - } else { - None - } + 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_ibc_denom(self) -> Option { - if let (Some(ref src), Some(ref dest)) = (self.src_chain, self.dest_chain) { - self.context - .ibc_denoms - .get(&(src.clone(), dest.clone())) - .cloned() - } else { - None - } + 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_admin_addr(self) -> Option { - if let Some(ref src) = self.src_chain { - self.context - .chains - .get(src) - .map(|chain| chain.admin_addr.clone()) - } else { - None - } + 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_native_denom(self) -> Option { - if let Some(ref src) = self.src_chain { - self.context - .chains - .get(src) - .map(|chain| chain.native_denom.clone()) - } else { - None - } + 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_chain_prefix(self) -> Option { - if let Some(ref src) = self.src_chain { - self.context - .chains - .get(src) - .map(|chain| chain.chain_prefix.clone()) - } else { - None + 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 = NEUTRON_CHAIN_ADMIN_ADDR; + let salt = self.salt_hex_encoded.as_deref()?; + + let chain = self + .context + .chains + .get(self.src_chain.as_deref().unwrap_or(NEUTRON_CHAIN_NAME))?; + + // text field contains built address + let mut resp = chain.rb.bin( + &format!("q wasm build-address {code_id_hash} {creator_address} {salt}"), + true, + ); + + match resp["text"].take() { + Value::String(s) => Some(s.replace("\n", "")), + _ => None, } } - fn get_rb(self) -> Option<&'a ChainRequestBuilder> { + 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 {