use serde::de::DeserializeOwned; use std::marker::PhantomData; use cosmwasm_std::{ from_slice, testing::{mock_env, BankQuerier, MockQuerierCustomHandlerResult, MockStorage}, Addr, Api, Binary, CanonicalAddr, Coin, ContractResult, CustomQuery, Empty, Env, OwnedDeps, Querier, QuerierResult, QueryRequest, RecoverPubkeyError, StdError, StdResult, SystemError, SystemResult, VerificationError, WasmQuery, }; use wormhole_bindings::WormholeQuery; pub const WORMHOLE_CONTRACT_ADDR: &str = "wormhole1yw4wv2zqg9xkn67zvq3azye0t8h0x9kgyg3d53jym24gxt49vdys6s8h7a"; pub const WORMHOLE_USER_ADDR: &str = "wormhole1vhkm2qv784rulx8ylru0zpvyvw3m3cy99e6wy0"; pub const WORMHOLE_CONTRACT_ADDR_BYTES: [u8; 32] = [ 0x23, 0xaa, 0xe6, 0x28, 0x40, 0x41, 0x4d, 0x69, 0xeb, 0xc2, 0x60, 0x23, 0xd1, 0x13, 0x2f, 0x59, 0xee, 0xf3, 0x16, 0xc8, 0x22, 0x22, 0xda, 0x46, 0x44, 0xda, 0xaa, 0x83, 0x2e, 0xa5, 0x63, 0x49, ]; pub const WORMHOLE_USER_ADDR_BYTES: [u8; 20] = [ 0x65, 0xed, 0xb5, 0x01, 0x9e, 0x3d, 0x47, 0xcf, 0x98, 0xe4, 0xf8, 0xf8, 0xf1, 0x05, 0x84, 0x63, 0xa3, 0xb8, 0xe0, 0x85, ]; // Custom API mock implementation for testing. // The custom impl helps us with correct addr_validate, addr_canonicalize, and addr_humanize methods for Wormchain. #[derive(Clone)] pub struct CustomApi { contract_addr: String, user_addr: String, contract_addr_bin: Binary, user_addr_bin: Binary, } impl CustomApi { pub fn new( contract_addr: &str, user_addr: &str, contract_addr_bytes: [u8; 32], user_addr_bytes: [u8; 20], ) -> Self { CustomApi { contract_addr: contract_addr.to_string(), user_addr: user_addr.to_string(), contract_addr_bin: Binary::from(contract_addr_bytes), user_addr_bin: Binary::from(user_addr_bytes), } } } impl Api for CustomApi { fn addr_validate(&self, input: &str) -> StdResult { if input == self.contract_addr { return Ok(Addr::unchecked(self.contract_addr.clone())); } if input == self.user_addr { return Ok(Addr::unchecked(self.user_addr.clone())); } Err(StdError::GenericErr { msg: "case not found".to_string(), }) } fn addr_canonicalize(&self, input: &str) -> StdResult { if input == self.contract_addr { return Ok(CanonicalAddr(self.contract_addr_bin.clone())); } if input == self.user_addr { return Ok(CanonicalAddr(self.user_addr_bin.clone())); } Err(StdError::GenericErr { msg: "case not found".to_string(), }) } fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult { if *canonical == self.contract_addr_bin { return Ok(Addr::unchecked(self.contract_addr.clone())); } if *canonical == self.user_addr_bin { return Ok(Addr::unchecked(self.user_addr.clone())); } Err(StdError::GenericErr { msg: "case not found".to_string(), }) } fn secp256k1_verify( &self, message_hash: &[u8], signature: &[u8], public_key: &[u8], ) -> Result { Ok(cosmwasm_crypto::secp256k1_verify( message_hash, signature, public_key, )?) } fn secp256k1_recover_pubkey( &self, message_hash: &[u8], signature: &[u8], recovery_param: u8, ) -> Result, RecoverPubkeyError> { let pubkey = cosmwasm_crypto::secp256k1_recover_pubkey(message_hash, signature, recovery_param)?; Ok(pubkey.to_vec()) } fn ed25519_verify( &self, message: &[u8], signature: &[u8], public_key: &[u8], ) -> Result { Ok(cosmwasm_crypto::ed25519_verify( message, signature, public_key, )?) } fn ed25519_batch_verify( &self, messages: &[&[u8]], signatures: &[&[u8]], public_keys: &[&[u8]], ) -> Result { Ok(cosmwasm_crypto::ed25519_batch_verify( messages, signatures, public_keys, )?) } fn debug(&self, message: &str) { println!("{message}"); } } #[allow(dead_code)] pub fn default_custom_mock_deps() -> OwnedDeps { OwnedDeps { storage: MockStorage::default(), api: CustomApi::new( WORMHOLE_CONTRACT_ADDR, WORMHOLE_USER_ADDR, WORMHOLE_CONTRACT_ADDR_BYTES, WORMHOLE_USER_ADDR_BYTES, ), querier: MockQuerier::default(), custom_query_type: PhantomData, } } #[allow(dead_code)] pub fn execute_custom_mock_deps() -> OwnedDeps { OwnedDeps { storage: MockStorage::default(), api: CustomApi::new( WORMHOLE_CONTRACT_ADDR, WORMHOLE_USER_ADDR, WORMHOLE_CONTRACT_ADDR_BYTES, WORMHOLE_USER_ADDR_BYTES, ), querier: MockQuerier::default(), custom_query_type: PhantomData, } } #[allow(dead_code)] pub fn mock_env_custom_contract(contract_addr: impl Into) -> Env { let mut env = mock_env(); env.contract.address = Addr::unchecked(contract_addr); env } /// MockQuerier holds an immutable table of bank balances /// and configurable handlers for Wasm queries and custom queries. pub struct MockQuerier { bank: BankQuerier, #[cfg(feature = "staking")] staking: StakingQuerier, wasm: WasmQuerier, #[cfg(feature = "stargate")] ibc: IbcQuerier, /// A handler to handle custom queries. This is set to a dummy handler that /// always errors by default. Update it via `with_custom_handler`. /// /// Use box to avoid the need of another generic type custom_handler: Box Fn(&'a C) -> MockQuerierCustomHandlerResult>, } impl MockQuerier { pub fn new(balances: &[(&str, &[Coin])]) -> Self { MockQuerier { bank: BankQuerier::new(balances), #[cfg(feature = "staking")] staking: StakingQuerier::default(), wasm: WasmQuerier::default(), #[cfg(feature = "stargate")] ibc: IbcQuerier::default(), // strange argument notation suggested as a workaround here: https://github.com/rust-lang/rust/issues/41078#issuecomment-294296365 custom_handler: Box::from(|_: &_| -> MockQuerierCustomHandlerResult { SystemResult::Ok(ContractResult::Ok(Binary::from_base64("e30=").unwrap())) }), } } // set a new balance for the given address and return the old balance #[allow(dead_code)] pub fn update_balance( &mut self, addr: impl Into, balance: Vec, ) -> Option> { self.bank.update_balance(addr, balance) } #[cfg(feature = "staking")] pub fn update_staking( &mut self, denom: &str, validators: &[crate::query::Validator], delegations: &[crate::query::FullDelegation], ) { self.staking = StakingQuerier::new(denom, validators, delegations); } #[cfg(feature = "stargate")] pub fn update_ibc(&mut self, port_id: &str, channels: &[IbcChannel]) { self.ibc = IbcQuerier::new(port_id, channels); } pub fn update_wasm(&mut self, handler: WH) where WH: Fn(&WasmQuery) -> QuerierResult, { self.wasm.update_handler(handler) } #[allow(dead_code)] pub fn with_custom_handler(mut self, handler: CH) -> Self where CH: Fn(&C) -> MockQuerierCustomHandlerResult, { self.custom_handler = Box::from(handler); self } } impl Default for MockQuerier { fn default() -> Self { MockQuerier::new(&[]) } } impl Querier for MockQuerier { fn raw_query(&self, bin_request: &[u8]) -> QuerierResult { let request: QueryRequest = match from_slice(bin_request) { Ok(v) => v, Err(e) => { return SystemResult::Err(SystemError::InvalidRequest { error: format!("Parsing query request: {e}"), request: bin_request.into(), }) } }; self.handle_query(&request) } } impl MockQuerier { pub fn handle_query(&self, request: &QueryRequest) -> QuerierResult { match &request { QueryRequest::Bank(bank_query) => self.bank.query(bank_query), QueryRequest::Custom(custom_query) => (*self.custom_handler)(custom_query), #[cfg(feature = "staking")] QueryRequest::Staking(staking_query) => self.staking.query(staking_query), QueryRequest::Wasm(msg) => self.wasm.query(msg), #[cfg(feature = "stargate")] QueryRequest::Stargate { .. } => SystemResult::Err(SystemError::UnsupportedRequest { kind: "Stargate".to_string(), }), #[cfg(feature = "stargate")] QueryRequest::Ibc(msg) => self.ibc.query(msg), //_ => SystemResult::Err(SystemError::UnsupportedRequest { // kind: "Unknown".to_string(), //}), _ => SystemResult::Ok(ContractResult::Ok(Binary::default())), } } } struct WasmQuerier { /// A handler to handle Wasm queries. This is set to a dummy handler that /// always errors by default. Update it via `with_custom_handler`. /// /// Use box to avoid the need of generic type. handler: Box Fn(&'a WasmQuery) -> QuerierResult>, } impl WasmQuerier { fn new(handler: Box Fn(&'a WasmQuery) -> QuerierResult>) -> Self { Self { handler } } fn update_handler(&mut self, handler: WH) where WH: Fn(&WasmQuery) -> QuerierResult, { self.handler = Box::from(handler) } fn query(&self, request: &WasmQuery) -> QuerierResult { (*self.handler)(request) } } impl Default for WasmQuerier { fn default() -> Self { let handler = Box::from(|request: &WasmQuery| -> QuerierResult { let err = match request { WasmQuery::Smart { contract_addr, .. } => SystemError::NoSuchContract { addr: contract_addr.clone(), }, WasmQuery::Raw { contract_addr, .. } => SystemError::NoSuchContract { addr: contract_addr.clone(), }, WasmQuery::ContractInfo { contract_addr, .. } => SystemError::NoSuchContract { addr: contract_addr.clone(), }, #[cfg(feature = "cosmwasm_1_2")] WasmQuery::CodeInfo { code_id, .. } => { SystemError::NoSuchCode { code_id: *code_id } } _ => SystemError::Unknown {}, }; SystemResult::Err(err) }); Self::new(handler) } }