diff --git a/cw-orch-daemon/src/proto/injective.rs b/cw-orch-daemon/src/proto/injective.rs index 1cf99348b..bdbf051a2 100644 --- a/cw-orch-daemon/src/proto/injective.rs +++ b/cw-orch-daemon/src/proto/injective.rs @@ -29,7 +29,7 @@ pub struct InjectivePubKey { impl Name for InjectivePubKey { const NAME: &'static str = "PubKey"; - const PACKAGE: &'static str = "/injective.crypto.v1beta1.ethsecp256k1"; + const PACKAGE: &'static str = "injective.crypto.v1beta1.ethsecp256k1"; /// Workaround until tokio-rs/prost#923 is released fn full_name() -> String { diff --git a/cw-orch/Cargo.toml b/cw-orch/Cargo.toml index 174f215d3..9510f50f5 100644 --- a/cw-orch/Cargo.toml +++ b/cw-orch/Cargo.toml @@ -27,6 +27,10 @@ required-features = ["daemon"] name = "testnet_daemon" required-features = ["daemon"] +[[example]] +name = "complex_testnet_daemon" +required-features = ["daemon", "osmosis-test-tube"] + [[example]] name = "osmosis_test_tube" required-features = ["osmosis-test-tube"] @@ -66,6 +70,13 @@ osmosis-test-tube = [ "dep:prost-types", "dep:cosmrs", ] +injective-test-tube = [ + "dep:injective-test-tube", + "dep:injective-std", + "dep:prost", + "dep:prost-types", + "eth", +] snapshot-testing = ["dep:insta", "dep:sanitize-filename"] [dependencies] @@ -106,6 +117,8 @@ schemars = "0.8.10" log = { workspace = true } thiserror = { workspace = true } hex = "0.4.3" +injective-test-tube = { version = "1.1.6", optional = true } +injective-std = { version = "0.1.4", optional = true } # Env deps # This packages will most likely make wasm not compilable diff --git a/cw-orch/src/injective_test_tube/core.rs b/cw-orch/src/injective_test_tube/core.rs new file mode 100644 index 000000000..5c9482b48 --- /dev/null +++ b/cw-orch/src/injective_test_tube/core.rs @@ -0,0 +1,339 @@ +use std::sync::Arc; + +use crate::contract::WasmPath; +use crate::mock::cw_multi_test::AppResponse; +use crate::prelude::Uploadable; +use cosmwasm_std::Addr; +use cosmwasm_std::{Binary, Coin, Uint128}; +use injective_std::shim::{cosmwasm_to_proto_coins, try_proto_to_cosmwasm_coins}; +use injective_std::types::cosmos::bank::v1beta1::{ + MsgSend, QueryAllBalancesRequest, QueryBalanceRequest, +}; + +use injective_test_tube::{Account, Bank, Module, RunnerError, SigningAccount, Wasm}; + +use injective_test_tube::InjectiveTestApp; +use std::{cell::RefCell, fmt::Debug, rc::Rc}; + +use serde::Serialize; + +use crate::{ + environment::TxHandler, + environment::{ChainState, StateInterface}, + error::CwOrchError, +}; + +use crate::mock::MockState; + +pub use injective_test_tube; + +/// Wrapper around a injective-test-tube [`InjectiveTestApp`](injective_test_tube::InjectiveTestApp) backend. +/// +/// Stores a local state with a mapping of contract_id -> code_id/address +/// +/// The state is customizable by implementing the [`StateInterface`] trait on a custom struct and providing it on the custom constructor. +/// +/// ## Example +/// ``` +/// # use cosmwasm_std::{Addr, coins, Uint128}; +/// use cw_orch::injective_test_tube::InjectiveTestTube; +/// use cw_orch::injective_test_tube::injective_test_tube::Account; +/// +/// // Creates an app, creates a sender with an initial balance +/// let mut tube: InjectiveTestTube = InjectiveTestTube::new(coins(1_000_000_000_000, "uosmo")); +/// +/// // create an additional account +/// let account = tube.init_account(coins(1_000_000_000, "uatom")).unwrap(); +/// +/// // query the balance +/// let balance: Uint128 = tube.query_balance(&account.address(), "uatom").unwrap(); +/// assert_eq!(balance.u128(), 1_000_000_000u128); +/// ``` +#[derive(Clone)] +pub struct InjectiveTestTube { + /// Address used for the operations. + pub sender: Rc, + /// Inner mutable state storage for contract addresses and code-ids + pub state: Rc>, + /// Inner mutable cw-multi-test app backend + pub app: Rc>, +} + +/// Maps runner error error to OrchError +pub fn map_err(e: RunnerError) -> CwOrchError { + CwOrchError::StdErr(e.to_string()) +} + +impl InjectiveTestTube { + /// Creates an account and sets its balance + pub fn init_account( + &mut self, + amount: Vec, + ) -> Result<::Sender, CwOrchError> { + let account = self + .app + .borrow() + .init_account(&amount) + .map_err(map_err) + .map(Rc::new)?; + + Ok(account) + } + + /// Creates accounts and sets their balance + pub fn init_accounts( + &mut self, + amount: Vec, + account_n: u64, + ) -> Result>, CwOrchError> { + let accounts: Vec<_> = self + .app + .borrow() + .init_accounts(&amount, account_n) + .map_err(map_err) + .map(|s| s.into_iter().map(Arc::new).collect())?; + + Ok(accounts) + } + + /// Sends coins a specific address + pub fn bank_send( + &self, + to: String, + amount: Vec, + ) -> Result { + let send_response = Bank::new(&*self.app.borrow()) + .send( + MsgSend { + from_address: self.sender.address(), + to_address: to, + amount: cosmwasm_to_proto_coins(amount), + }, + &self.sender, + ) + .map_err(map_err)?; + + Ok(AppResponse { + data: Some(Binary(send_response.raw_data)), + events: send_response.events, + }) + } + + /// Query the (bank) balance of a native token for and address. + /// Returns the amount of the native token. + pub fn query_balance(&self, address: &str, denom: &str) -> Result { + let amount = try_proto_to_cosmwasm_coins( + Bank::new(&*self.app.borrow()) + .query_balance(&QueryBalanceRequest { + address: address.to_owned(), + denom: denom.to_string(), + }) + .map_err(map_err)? + .balance, + )? + .first() + .map(|c| c.amount) + .unwrap_or(Uint128::zero()); + Ok(amount) + } + + /// Fetch all the balances of an address. + pub fn query_all_balances( + &self, + address: &str, + ) -> Result, CwOrchError> { + let amount = try_proto_to_cosmwasm_coins( + Bank::new(&*self.app.borrow()) + .query_all_balances(&QueryAllBalancesRequest { + address: address.to_owned(), + pagination: None, + }) + .map_err(map_err)? + .balances, + )?; + Ok(amount) + } +} + +impl InjectiveTestTube { + /// Create a mock environment with the default mock state. + /// init_coins are minted to the sender that is created in the InjectiveTestTube environment + /// Unlike for mocks, the accounts are created by the struct and not provided by the client + /// Make sure to use only valid bech32 osmosis addresses, not mock + pub fn new(init_coins: Vec) -> Self { + Self::new_custom(init_coins, MockState::new()) + } +} + +impl InjectiveTestTube { + /// Create a mock environment with a custom mock state. + /// The state is customizable by implementing the `StateInterface` trait on a custom struct and providing it on the custom constructor. + pub fn new_custom(init_coins: Vec, custom_state: S) -> Self { + let state = Rc::new(RefCell::new(custom_state)); + let app = Rc::new(RefCell::new(InjectiveTestApp::new())); + + let sender = app.borrow().init_account(&init_coins).unwrap(); + + Self { + sender: Rc::new(sender), + state, + app, + } + } +} + +impl ChainState for InjectiveTestTube { + type Out = Rc>; + + fn state(&self) -> Self::Out { + self.state.clone() + } +} + +// Execute on the test chain, returns test response type +impl TxHandler for InjectiveTestTube { + type Error = CwOrchError; + type ContractSource = WasmPath; + type Response = AppResponse; + type Sender = Rc; + + fn sender(&self) -> Addr { + Addr::unchecked(self.sender.address()) + } + + fn set_sender(&mut self, sender: Self::Sender) { + self.sender = sender; + } + + fn upload(&self, contract: &impl Uploadable) -> Result { + let wasm_contents = std::fs::read(contract.wasm().path())?; + let upload_response = Wasm::new(&*self.app.borrow()) + .store_code(&wasm_contents, None, &self.sender) + .map_err(map_err)?; + + Ok(AppResponse { + data: Some(Binary(upload_response.raw_data)), + events: upload_response.events, + }) + } + + fn execute( + &self, + exec_msg: &E, + coins: &[cosmwasm_std::Coin], + contract_address: &Addr, + ) -> Result { + let execute_response = Wasm::new(&*self.app.borrow()) + .execute(contract_address.as_ref(), exec_msg, coins, &self.sender) + .map_err(map_err)?; + + Ok(AppResponse { + data: Some(Binary(execute_response.raw_data)), + events: execute_response.events, + }) + } + + fn instantiate( + &self, + code_id: u64, + init_msg: &I, + label: Option<&str>, + admin: Option<&Addr>, + coins: &[cosmwasm_std::Coin], + ) -> Result { + let instantiate_response = Wasm::new(&*self.app.borrow()) + .instantiate( + code_id, + init_msg, + admin.map(|a| a.to_string()).as_deref(), + label, + coins, + &self.sender, + ) + .map_err(map_err)?; + + Ok(AppResponse { + data: Some(Binary(instantiate_response.raw_data)), + events: instantiate_response.events, + }) + } + + fn migrate( + &self, + _migrate_msg: &M, + _new_code_id: u64, + _contract_address: &Addr, + ) -> Result { + panic!("Migrate not implemented on osmosis test_tube") + } + + fn instantiate2( + &self, + _code_id: u64, + _init_msg: &I, + _label: Option<&str>, + _admin: Option<&Addr>, + _coins: &[cosmwasm_std::Coin], + _salt: Binary, + ) -> Result { + unimplemented!("Injective Test Tube doesn't support Instantiate 2 directly"); + } +} + +#[cfg(test)] +pub mod tests { + use cosmwasm_std::{coins, ContractInfoResponse}; + use cw_orch_core::environment::BankQuerier; + + use injective_test_tube::Account; + + use super::InjectiveTestTube; + use counter_contract::{msg::InstantiateMsg, CounterContract}; + use cw_orch::prelude::*; + + #[test] + fn wasm_querier_works() -> anyhow::Result<()> { + let app = InjectiveTestTube::new(coins(100_000_000_000_000, "inj")); + + let contract = CounterContract::new(app.clone()); + contract.upload()?; + contract.instantiate( + &InstantiateMsg { count: 7 }, + Some(&Addr::unchecked(app.sender.address())), + None, + )?; + + assert_eq!( + contract.wasm().checksum()?, + app.wasm_querier().code_id_hash(contract.code_id()?)? + ); + + let contract_info = app.wasm_querier().contract_info(contract.address()?)?; + let mut target_contract_info = ContractInfoResponse::default(); + target_contract_info.admin = Some(app.sender.address().to_string()); + target_contract_info.code_id = contract.code_id()?; + target_contract_info.creator = app.sender.address().to_string(); + target_contract_info.ibc_port = None; + assert_eq!(contract_info, target_contract_info); + + Ok(()) + } + + #[test] + fn bank_querier_works() -> anyhow::Result<()> { + let denom = "urandom"; + let init_coins = coins(45, denom); + let app = InjectiveTestTube::new(init_coins.clone()); + let sender = app.sender.address(); + assert_eq!( + app.bank_querier() + .balance(sender.clone(), Some(denom.to_string()))?, + init_coins + ); + assert_eq!( + app.bank_querier().supply_of(denom.to_string())?, + init_coins[0] + ); + Ok(()) + } +} diff --git a/cw-orch/src/injective_test_tube/mod.rs b/cw-orch/src/injective_test_tube/mod.rs new file mode 100644 index 000000000..3bf3e63f6 --- /dev/null +++ b/cw-orch/src/injective_test_tube/mod.rs @@ -0,0 +1,7 @@ +//! Integration testing execution environment backed by a [injective-test-tube](injective_test_tube) App. +//! It has an associated state that stores deployment information for easy retrieval and contract interactions. + +mod core; + +mod queriers; +pub use self::core::*; diff --git a/cw-orch/src/injective_test_tube/queriers/bank.rs b/cw-orch/src/injective_test_tube/queriers/bank.rs new file mode 100644 index 000000000..ca4befe5a --- /dev/null +++ b/cw-orch/src/injective_test_tube/queriers/bank.rs @@ -0,0 +1,104 @@ +use std::{cell::RefCell, rc::Rc}; + +use cosmwasm_std::coin; +use cw_orch_core::{ + environment::{ + Querier, StateInterface, {BankQuerier, QuerierGetter}, + }, + CwEnvError, +}; +use injective_std::{ + shim::try_proto_to_cosmwasm_coins, + types::cosmos::bank::v1beta1::{QuerySupplyOfRequest, QuerySupplyOfResponse}, +}; +use injective_test_tube::{Bank, InjectiveTestApp, Module, Runner}; + +use crate::injective_test_tube::core::map_err; +use crate::injective_test_tube::InjectiveTestTube; +use injective_std::types::cosmos::bank::v1beta1::{QueryAllBalancesRequest, QueryBalanceRequest}; +pub struct InjectiveTestTubeBankQuerier { + app: Rc>, +} + +impl InjectiveTestTubeBankQuerier { + fn new(mock: &InjectiveTestTube) -> Self { + Self { + app: mock.app.clone(), + } + } +} + +impl Querier for InjectiveTestTubeBankQuerier { + type Error = CwEnvError; +} + +impl QuerierGetter for InjectiveTestTube { + fn querier(&self) -> InjectiveTestTubeBankQuerier { + InjectiveTestTubeBankQuerier::new(self) + } +} + +impl BankQuerier for InjectiveTestTubeBankQuerier { + fn balance( + &self, + address: impl Into, + denom: Option, + ) -> Result, Self::Error> { + if let Some(denom) = denom { + let amount = Bank::new(&*self.app.borrow()) + .query_balance(&QueryBalanceRequest { + address: address.into(), + denom: denom.to_string(), + }) + .map_err(map_err)? + .balance + .map(|c| { + let coins = try_proto_to_cosmwasm_coins(vec![c])?[0].clone(); + Ok::<_, CwEnvError>(coins) + }) + .transpose()? + .unwrap_or(coin(0, &denom)); + Ok(vec![amount]) + } else { + let amount = Bank::new(&*self.app.borrow()) + .query_all_balances(&QueryAllBalancesRequest { + address: address.into(), + pagination: None, + }) + .map_err(map_err)? + .balances; + + Ok(try_proto_to_cosmwasm_coins(amount)?) + } + } + + fn supply_of(&self, denom: impl Into) -> Result { + let denom: String = denom.into(); + let supply_of_result: QuerySupplyOfResponse = self + .app + .borrow() + .query( + "/cosmos.bank.v1beta1.Query/SupplyOf", + &QuerySupplyOfRequest { + denom: denom.clone(), + }, + ) + .map_err(map_err)?; + + Ok(supply_of_result + .amount + .map(|c| { + // Ok::<_, StdError>(cosmwasm_std::Coin { + // amount: c.amount.parse()?, + // denom: c.denom, + // }) + Ok::<_, CwEnvError>(try_proto_to_cosmwasm_coins(vec![c])?[0].clone()) + }) + .transpose()? + .unwrap_or(coin(0, &denom))) + } + + fn total_supply(&self) -> Result, Self::Error> { + unimplemented!() + } +} diff --git a/cw-orch/src/injective_test_tube/queriers/env.rs b/cw-orch/src/injective_test_tube/queriers/env.rs new file mode 100644 index 000000000..6a733e2b6 --- /dev/null +++ b/cw-orch/src/injective_test_tube/queriers/env.rs @@ -0,0 +1,19 @@ +use cw_orch_core::environment::{ + EnvironmentInfo, EnvironmentQuerier, QueryHandler, StateInterface, +}; + +use crate::injective_test_tube::InjectiveTestTube; + +impl EnvironmentQuerier for InjectiveTestTube { + fn env_info(&self) -> EnvironmentInfo { + let block = self.block_info().unwrap(); + let chain_id = block.chain_id; + let chain_name = chain_id.rsplitn(2, '-').collect::>()[1].to_string(); + + EnvironmentInfo { + chain_id, + chain_name, + deployment_id: "default".to_string(), + } + } +} diff --git a/cw-orch/src/injective_test_tube/queriers/mod.rs b/cw-orch/src/injective_test_tube/queriers/mod.rs new file mode 100644 index 000000000..3d41d35ab --- /dev/null +++ b/cw-orch/src/injective_test_tube/queriers/mod.rs @@ -0,0 +1,33 @@ +use crate::error::CwOrchError; + +use cw_orch_core::environment::{DefaultQueriers, QueryHandler, StateInterface}; + +use super::InjectiveTestTube; + +pub mod bank; +mod env; +pub mod node; +pub mod wasm; + +impl QueryHandler for InjectiveTestTube { + type Error = CwOrchError; + + fn wait_blocks(&self, _amount: u64) -> Result<(), CwOrchError> { + panic!("Can't wait blocks on osmosis_test_tube") + } + + fn wait_seconds(&self, secs: u64) -> Result<(), CwOrchError> { + self.app.borrow().increase_time(secs); + Ok(()) + } + + fn next_block(&self) -> Result<(), CwOrchError> { + panic!("Can't wait blocks on osmosis_test_tube") + } +} + +impl DefaultQueriers for InjectiveTestTube { + type Bank = bank::InjectiveTestTubeBankQuerier; + type Wasm = wasm::InjectiveTestTubeWasmQuerier; + type Node = node::InjectiveTestTubeNodeQuerier; +} diff --git a/cw-orch/src/injective_test_tube/queriers/node.rs b/cw-orch/src/injective_test_tube/queriers/node.rs new file mode 100644 index 000000000..d6fc8905a --- /dev/null +++ b/cw-orch/src/injective_test_tube/queriers/node.rs @@ -0,0 +1,64 @@ +use std::{cell::RefCell, rc::Rc}; + +use crate::mock::cw_multi_test::AppResponse; +use cosmwasm_std::{BlockInfo, Timestamp}; +use cw_orch_core::{ + environment::{NodeQuerier, Querier, QuerierGetter, StateInterface}, + CwEnvError, +}; +use injective_test_tube::InjectiveTestApp; + +use crate::injective_test_tube::InjectiveTestTube; + +pub struct InjectiveTestTubeNodeQuerier { + app: Rc>, +} + +impl InjectiveTestTubeNodeQuerier { + fn new(mock: &InjectiveTestTube) -> Self { + Self { + app: mock.app.clone(), + } + } +} + +impl Querier for InjectiveTestTubeNodeQuerier { + type Error = CwEnvError; +} + +impl QuerierGetter for InjectiveTestTube { + fn querier(&self) -> InjectiveTestTubeNodeQuerier { + InjectiveTestTubeNodeQuerier::new(self) + } +} + +impl NodeQuerier for InjectiveTestTubeNodeQuerier { + type Response = AppResponse; + fn latest_block(&self) -> Result { + Ok(BlockInfo { + chain_id: "injective-1".to_string(), + height: self.block_height()?, + time: Timestamp::from_nanos(self.block_time()?.try_into().unwrap()), + }) + } + + fn block_by_height(&self, _height: u64) -> Result { + unimplemented!() + } + + fn block_height(&self) -> Result { + Ok(self.app.borrow().get_block_height().try_into().unwrap()) + } + + fn block_time(&self) -> Result { + Ok(self.app.borrow().get_block_time_nanos().try_into().unwrap()) + } + + fn simulate_tx(&self, _tx_bytes: Vec) -> Result { + unimplemented!() + } + + fn find_tx(&self, _hash: String) -> Result { + unimplemented!() + } +} diff --git a/cw-orch/src/injective_test_tube/queriers/wasm.rs b/cw-orch/src/injective_test_tube/queriers/wasm.rs new file mode 100644 index 000000000..cc59a4445 --- /dev/null +++ b/cw-orch/src/injective_test_tube/queriers/wasm.rs @@ -0,0 +1,176 @@ +use std::{cell::RefCell, rc::Rc, str::FromStr}; + +use cosmrs::AccountId; +use cosmwasm_std::{ + from_json, instantiate2_address, to_json_vec, CanonicalAddr, CodeInfoResponse, + ContractInfoResponse, HexBinary, +}; +use cw_orch_core::{ + environment::{Querier, QuerierGetter, StateInterface, WasmQuerier}, + CwEnvError, +}; +use injective_test_tube::{InjectiveTestApp, Runner}; + +use crate::{injective_test_tube::map_err, injective_test_tube::InjectiveTestTube}; +use injective_test_tube::cosmrs::proto::cosmwasm::wasm::v1::{ + QueryCodeRequest, QueryCodeResponse, QueryContractInfoRequest, QueryContractInfoResponse, + QueryRawContractStateRequest, QueryRawContractStateResponse, QuerySmartContractStateRequest, + QuerySmartContractStateResponse, +}; + +pub struct InjectiveTestTubeWasmQuerier { + app: Rc>, +} + +impl InjectiveTestTubeWasmQuerier { + fn new(mock: &InjectiveTestTube) -> Self { + Self { + app: mock.app.clone(), + } + } +} + +impl Querier for InjectiveTestTubeWasmQuerier { + type Error = CwEnvError; +} + +impl QuerierGetter for InjectiveTestTube { + fn querier(&self) -> InjectiveTestTubeWasmQuerier { + InjectiveTestTubeWasmQuerier::new(self) + } +} + +impl WasmQuerier for InjectiveTestTubeWasmQuerier { + fn code_id_hash(&self, code_id: u64) -> Result { + let code_info_result: QueryCodeResponse = self + .app + .borrow() + .query( + "/cosmwasm.wasm.v1.Query/Code", + &QueryCodeRequest { code_id }, + ) + .map_err(map_err)?; + + Ok(code_info_result + .code_info + .ok_or(CwEnvError::CodeIdNotInStore(code_id.to_string()))? + .data_hash + .into()) + } + + fn contract_info( + &self, + address: impl Into, + ) -> Result { + let address = address.into(); + let result = self + .app + .borrow() + .query::<_, QueryContractInfoResponse>( + "/cosmwasm.wasm.v1.Query/ContractInfo", + &QueryContractInfoRequest { + address: address.clone(), + }, + ) + .map_err(map_err)? + .contract_info + .ok_or(CwEnvError::AddrNotInStore(address))?; + + let mut contract_info = ContractInfoResponse::default(); + contract_info.code_id = result.code_id; + contract_info.creator = result.creator; + contract_info.admin = Some(result.admin); + + contract_info.ibc_port = if result.ibc_port_id.is_empty() { + None + } else { + Some(result.ibc_port_id) + }; + + Ok(contract_info) + } + + fn raw_query( + &self, + address: impl Into, + query_data: Vec, + ) -> Result, Self::Error> { + let address = address.into(); + let result = self + .app + .borrow() + .query::<_, QueryRawContractStateResponse>( + "/cosmwasm.wasm.v1.Query/RawContractState", + &QueryRawContractStateRequest { + address: address.clone(), + query_data, + }, + ) + .map_err(map_err)? + .data; + + Ok(result) + } + + fn smart_query( + &self, + address: impl Into, + query_data: &Q, + ) -> Result { + let address = address.into(); + let result = self + .app + .borrow() + .query::<_, QuerySmartContractStateResponse>( + "/cosmwasm.wasm.v1.Query/SmartContractState", + &QuerySmartContractStateRequest { + address: address.clone(), + query_data: to_json_vec(query_data)?, + }, + ) + .map_err(map_err)? + .data; + + Ok(from_json(result)?) + } + + fn code(&self, code_id: u64) -> Result { + let response: QueryCodeResponse = self + .app + .borrow() + .query( + "/cosmwasm.wasm.v1.Query/Code", + &QueryCodeRequest { code_id }, + ) + .map_err(map_err)?; + + let code_info = response + .code_info + .ok_or(CwEnvError::CodeIdNotInStore(code_id.to_string()))?; + + let mut c = CodeInfoResponse::default(); + c.code_id = code_id; + c.creator = code_info.creator; + c.checksum = code_info.data_hash.into(); + + Ok(c) + } + + fn instantiate2_addr( + &self, + code_id: u64, + creator: impl Into, + salt: cosmwasm_std::Binary, + ) -> Result { + let checksum = self.code_id_hash(code_id)?; + + let creator_str = creator.into(); + let account_id = AccountId::from_str(&creator_str).unwrap(); + let prefix = account_id.prefix(); + let canon = account_id.to_bytes(); + let addr = + instantiate2_address(checksum.as_slice(), &CanonicalAddr(canon.into()), &salt).unwrap(); + + Ok(AccountId::new(prefix, &addr.0).unwrap().to_string()) + } +} diff --git a/cw-orch/src/lib.rs b/cw-orch/src/lib.rs index 634aa8646..4369817e5 100644 --- a/cw-orch/src/lib.rs +++ b/cw-orch/src/lib.rs @@ -17,6 +17,10 @@ mod error; #[cfg(feature = "daemon")] pub mod daemon; +#[cfg(not(target_arch = "wasm32"))] +#[cfg(feature = "injective-test-tube")] +pub mod injective_test_tube; + #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "osmosis-test-tube")] pub mod osmosis_test_tube; diff --git a/cw-orch/tests/interface_macro_lifetime.rs b/cw-orch/tests/interface_macro_lifetime.rs new file mode 100644 index 000000000..457afa9e6 --- /dev/null +++ b/cw-orch/tests/interface_macro_lifetime.rs @@ -0,0 +1,181 @@ +// use cosmwasm_schema::{cw_serde, QueryResponses}; +// use cw_orch::{environment::CwEnv, interface, prelude::*}; + +// use cosmwasm_std::{ +// to_json_binary, Addr, Binary, Deps, DepsMut, Env, Event, MessageInfo, Response, StdError, +// StdResult, +// }; +// use cw_orch::prelude::Mock; + +// #[cw_serde] +// pub struct InstantiateMsg<'a> { +// addr: &'a str, +// } + +// #[cw_serde] +// #[cfg_attr(feature = "interface", derive(cw_orch::ExecuteFns))] +// pub enum ExecuteMsg<'a> { +// FirstMessage { addr: &'a str }, +// } + +// #[cw_serde] +// #[cfg_attr(feature = "interface", derive(cw_orch::QueryFns))] +// #[derive(QueryResponses)] +// pub enum QueryMsg<'a> { +// #[returns(String)] +// /// test-doc-comment +// FirstQuery { addr: &'a str }, +// } + +// #[cw_serde] +// pub struct MigrateMsg<'a> { +// pub addr: &'a str, +// } + +// pub fn instantiate( +// _deps: DepsMut, +// _env: Env, +// _info: MessageInfo, +// _msg: InstantiateMsg, +// ) -> StdResult { +// Ok(Response::new().add_attribute("action", "instantiate")) +// } + +// pub fn execute( +// _deps: DepsMut, +// _env: Env, +// info: MessageInfo, +// msg: ExecuteMsg, +// ) -> StdResult { +// match msg { +// ExecuteMsg::FirstMessage { addr } => { +// Ok(Response::new().add_attribute("action", "first message passed")) +// } +// } +// } + +// #[cfg_attr(feature = "export", cosmwasm_std::entry_point)] +// pub fn query(_deps: Deps, _env: Env, msg: QueryMsg) -> StdResult { +// match msg { +// QueryMsg::FirstQuery { addr } => to_json_binary("first query passed"), +// } +// } + +// #[cfg_attr(feature = "export", cosmwasm_std::entry_point)] +// pub fn migrate(_deps: DepsMut, _env: Env, _msg: MigrateMsg) -> StdResult { +// Ok(Response::new()) +// } + +// #[interface(InstantiateMsg<'a>, ExecuteMsg<'a>, QueryMsg<'a>, MigrateMsg<'a>)] +// pub struct MacroContract; + +// impl<'a, Chain: CwEnv> Uploadable for MacroContract<'a, Chain> { +// fn wrapper(&self) -> ::ContractSource { +// Box::new(ContractWrapper::new_with_empty(execute, instantiate, query).with_migrate(migrate)) +// } +// } + +// #[test] +// fn test_instantiate() { +// let contract = MacroContract::new( +// "test:mock_contract", +// Mock::new(&Addr::unchecked("Ghazshag")), +// ); +// contract.upload().unwrap(); + +// contract +// .instantiate( +// &InstantiateMsg { +// addr: "abstract_user", +// }, +// None, +// None, +// ) +// .unwrap(); +// } + +// #[test] +// fn test_execute() { +// let contract = MacroContract::new( +// "test:mock_contract", +// Mock::new(&Addr::unchecked("Ghazshag")), +// ); +// contract.upload().unwrap(); + +// contract +// .instantiate( +// &InstantiateMsg { +// addr: "abstract_account", +// }, +// None, +// None, +// ) +// .unwrap(); + +// let response = contract +// .execute( +// &ExecuteMsg::FirstMessage { +// addr: "abstract_account", +// }, +// None, +// ) +// .unwrap(); + +// response.has_event( +// &Event::new("wasm") +// .add_attribute("_contract_addr", "contract0") +// .add_attribute("action", "first message passed"), +// ); +// } + +// #[test] +// fn test_query() { +// let contract = MacroContract::new( +// "test:mock_contract", +// Mock::new(&Addr::unchecked("Ghazshag")), +// ); +// contract.upload().unwrap(); + +// contract +// .instantiate( +// &InstantiateMsg { +// addr: "abstract_account", +// }, +// None, +// None, +// ) +// .unwrap(); + +// let response: String = contract +// .query(&QueryMsg::FirstQuery { +// addr: "abstract_account", +// }) +// .unwrap(); +// assert_eq!(response, "first query passed"); +// } + +// #[test] +// fn test_migrate() { +// let admin = Addr::unchecked("Ghazshag"); +// let contract = MacroContract::new("test:mock_contract", Mock::new(&admin)); +// contract.upload().unwrap(); + +// contract +// .instantiate( +// &InstantiateMsg { +// addr: "abstract_account", +// }, +// Some(&admin), +// None, +// ) +// .unwrap(); +// let response = contract +// .migrate( +// &MigrateMsg { +// addr: "abstract_account", +// }, +// contract.code_id().unwrap(), +// ) +// .unwrap(); +// assert_eq!(response.events.len(), 1); +// } diff --git a/docs/src/integrations/fork-testing.md b/docs/src/integrations/fork-testing.md index 008de512b..1c18f6e1a 100644 --- a/docs/src/integrations/fork-testing.md +++ b/docs/src/integrations/fork-testing.md @@ -14,9 +14,9 @@ Setting up the environment is really easy and only requires feeding a `ChainData ```rust,ignore use cw_orch::networks::JUNO_1; -use cw_orch::ForkMock; +use cw_orch::CloneTesting; -let app = ForkMock::new(JUNO_1)?; +let app = CloneTesting::new(JUNO_1)?; ``` With this, you are ready to upload, instantiate, migrate and interact with on-chain contracts... @@ -89,7 +89,7 @@ Let's take an example for clarity. Say I want to deposit some funds into Anchor You use this fork environment as you would use the `Mock` environment, with a few subtle changes: -1. You can't use human readable addresses, because this environment uses actual APIs and needs to be able to verify addresses. When creating the Mock environment, a sender gets created along and attach automatically to the `ForkMock` instance. If you need additional addresses, you can use: +1. You can't use human readable addresses, because this environment uses actual APIs and needs to be able to verify addresses. When creating the Mock environment, a sender gets created along and attach automatically to the `CloneTesting` instance. If you need additional addresses, you can use: ```rust,ignore let new_sender: Addr = fork.init_account();