added better sample test assertions + fixed MockApi
This commit is contained in:
Generated
+2
@@ -909,6 +909,7 @@ dependencies = [
|
||||
"base64 0.21.0",
|
||||
"cosmwasm-std",
|
||||
"cosmwasm-storage",
|
||||
"cw-storage-plus",
|
||||
"hex",
|
||||
"rand_chacha 0.3.1",
|
||||
"serde",
|
||||
@@ -2759,6 +2760,7 @@ version = "0.1.0"
|
||||
dependencies = [
|
||||
"cosmwasm-contract-testing",
|
||||
"cosmwasm-std",
|
||||
"cw-storage-plus",
|
||||
"mixnet-contract",
|
||||
"mixnet-contract-common",
|
||||
"vesting-contract",
|
||||
|
||||
@@ -7,6 +7,7 @@ edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
cosmwasm-std = "1.0.0"
|
||||
cw-storage-plus = "0.13.4"
|
||||
cosmwasm-contract-testing = { path = "../testing" }
|
||||
mixnet-contract-common = { path= "../mixnet-contract" }
|
||||
vesting-contract-common = { path= "../vesting-contract" }
|
||||
|
||||
@@ -6,8 +6,11 @@ use cosmwasm_contract_testing::{
|
||||
};
|
||||
use cosmwasm_std::testing::mock_info;
|
||||
use cosmwasm_std::{
|
||||
coin, BlockInfo, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Timestamp,
|
||||
Addr, BankMsg, BlockInfo, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response, Timestamp,
|
||||
};
|
||||
use cw_storage_plus::Map;
|
||||
use mixnet_contract_common::rewarding::PendingRewardResponse;
|
||||
use vesting_contract::vesting::Account;
|
||||
|
||||
struct VestingContract;
|
||||
|
||||
@@ -87,11 +90,15 @@ impl TestableContract for MixnetContract {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn multi_mock() {
|
||||
let mixnet_contract_address = "n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
|
||||
let vesting_contract_address = "n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
// this is not directly exported by the vesting contract, but we can easily recreate it
|
||||
const VESTING_ACCOUNTS: Map<'_, Addr, Account> = Map::new("acc");
|
||||
|
||||
const MIXNET_CONTRACT_ADDRESS: &str =
|
||||
"n14hj2tavq8fpesdwxxcu44rty3hh90vhujrvcmstl4zr3txmfvw9sjyvg3g";
|
||||
const VESTING_CONTRACT_ADDRESS: &str =
|
||||
"n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw";
|
||||
|
||||
fn set_mock() -> MultiContractMock {
|
||||
let current_block = BlockInfo {
|
||||
height: 1928125,
|
||||
time: Timestamp::from_seconds(1676482616),
|
||||
@@ -104,13 +111,13 @@ fn multi_mock() {
|
||||
Some(custom_env.clone()),
|
||||
)
|
||||
.unwrap()
|
||||
.with_contract_address(mixnet_contract_address);
|
||||
.with_contract_address(MIXNET_CONTRACT_ADDRESS);
|
||||
let vesting_mock = ContractState::try_from_state_dump(
|
||||
"contract-states/15.02.23-173000-qwerty-vesting.json",
|
||||
Some(custom_env),
|
||||
)
|
||||
.unwrap()
|
||||
.with_contract_address(vesting_contract_address);
|
||||
.with_contract_address(VESTING_CONTRACT_ADDRESS);
|
||||
|
||||
let mut multi_mock = MultiContractMock::new();
|
||||
|
||||
@@ -118,26 +125,86 @@ fn multi_mock() {
|
||||
multi_mock
|
||||
.add_contract::<VestingContract>(vesting_mock)
|
||||
.unwrap();
|
||||
multi_mock
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claiming_vesting_delegator_rewards() {
|
||||
let mut multi_mock = set_mock();
|
||||
|
||||
let dummy_account = Addr::unchecked("n1ktpuwtweku40uaxcl4uq7mdkkmjeh698g3l3c8");
|
||||
|
||||
// do some queries to verify state is updated correctly for both contracts
|
||||
let pending_reward: PendingRewardResponse = multi_mock
|
||||
.query::<MixnetContract, _>(
|
||||
MIXNET_CONTRACT_ADDRESS,
|
||||
mixnet_contract_common::QueryMsg::GetPendingDelegatorReward {
|
||||
address: dummy_account.to_string(),
|
||||
mix_id: 8,
|
||||
proxy: Some(VESTING_CONTRACT_ADDRESS.to_string()),
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
let pending_reward_amount = pending_reward.amount_earned.unwrap().amount;
|
||||
|
||||
// we can also get whatever we want directly from storage!
|
||||
let contract_state = multi_mock.contract_state(VESTING_CONTRACT_ADDRESS).unwrap();
|
||||
let vesting_account = contract_state
|
||||
.load_map_value(&VESTING_ACCOUNTS, dummy_account.clone())
|
||||
.unwrap();
|
||||
let vesting_balance = vesting_account
|
||||
.load_balance(contract_state.deps().storage)
|
||||
.unwrap();
|
||||
|
||||
let res = multi_mock.execute_full::<VestingContract>(
|
||||
vesting_contract_address,
|
||||
mock_info("n1vuz06p7cgagxcaplfezchvpu99u4np7erfxa4c", &[]),
|
||||
vesting_contract_common::ExecuteMsg::DelegateToMixnode {
|
||||
mix_id: 7,
|
||||
amount: coin(1000, "unym"),
|
||||
on_behalf_of: None,
|
||||
},
|
||||
VESTING_CONTRACT_ADDRESS,
|
||||
mock_info(dummy_account.as_str(), &[]),
|
||||
vesting_contract_common::ExecuteMsg::ClaimDelegatorReward { mix_id: 8 },
|
||||
);
|
||||
|
||||
match res {
|
||||
Ok(success) => {
|
||||
// first we should have emitted a "vesting_delegation" event from the vesting contract
|
||||
// followed by "v2_pending_delegation" from the mixnet contract
|
||||
assert_eq!("vesting_delegation", success.steps[0].events[0].ty);
|
||||
assert_eq!("v2_pending_delegation", success.steps[1].events[0].ty);
|
||||
println!("{}", success.pretty());
|
||||
|
||||
// println!("{}", success.pretty())
|
||||
// check the output
|
||||
|
||||
// unfortunately `ClaimDelegatorReward` doesn't emit any events, but we can see
|
||||
// it's going to result into a call into the mixnet contract
|
||||
assert_eq!(
|
||||
success.steps[0].further_execution[0].contract.as_str(),
|
||||
MIXNET_CONTRACT_ADDRESS
|
||||
);
|
||||
|
||||
// mixnet contract will emit a `v2_withdraw_delegator_reward` event
|
||||
// and call the vesting contract again
|
||||
assert_eq!(
|
||||
"v2_withdraw_delegator_reward",
|
||||
success.steps[1].events[0].ty
|
||||
);
|
||||
assert_eq!(
|
||||
success.steps[1].further_execution[0].contract.as_str(),
|
||||
VESTING_CONTRACT_ADDRESS
|
||||
);
|
||||
// and will move our reward amount into the vesting contract...
|
||||
assert!(matches!(
|
||||
&success.steps[1].bank_msgs[0],
|
||||
BankMsg::Send { to_address, amount }
|
||||
if to_address == VESTING_CONTRACT_ADDRESS && amount[0].amount == pending_reward_amount
|
||||
));
|
||||
|
||||
// and finally the vesting contract will emit the mistyped `track_reaward` event
|
||||
assert_eq!("track_reaward", success.steps[2].events[0].ty);
|
||||
}
|
||||
Err(err) => panic!("{err}"),
|
||||
}
|
||||
|
||||
// state after execution
|
||||
let updated_state = multi_mock.contract_state(VESTING_CONTRACT_ADDRESS).unwrap();
|
||||
let vesting_account = updated_state
|
||||
.load_map_value(&VESTING_ACCOUNTS, dummy_account.clone())
|
||||
.unwrap();
|
||||
let new_vesting_balance = vesting_account
|
||||
.load_balance(updated_state.deps().storage)
|
||||
.unwrap();
|
||||
assert_eq!(new_vesting_balance, vesting_balance + pending_reward_amount)
|
||||
}
|
||||
|
||||
@@ -9,8 +9,12 @@ edition = "2021"
|
||||
base64 = "0.21.0"
|
||||
cosmwasm-std = { version = "1.0.0" }
|
||||
cosmwasm-storage = { version = "1.0.0" }
|
||||
cw-storage-plus = { version = "0.13.4", optional = true }
|
||||
hex = "0.4.3"
|
||||
rand_chacha = "0.3"
|
||||
serde = { version = "1", features=["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "1.0.38"
|
||||
|
||||
[features]
|
||||
default = ["cw-storage-plus"]
|
||||
@@ -1,20 +1,27 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::mock_api::CW12MockApi;
|
||||
use crate::raw_state::{DecodingError, EncodingError, ImportedContractState, KeyValue};
|
||||
use crate::AVERAGE_BLOCKTIME_SECS;
|
||||
use cosmwasm_std::testing::{mock_env, MockApi, MockQuerier, MockStorage};
|
||||
use cosmwasm_std::testing::{mock_env, MockQuerier, MockStorage};
|
||||
use cosmwasm_std::{
|
||||
Addr, Coin, Deps, DepsMut, Env, Order, QuerierWrapper, StdResult, Storage, Timestamp,
|
||||
TransactionInfo,
|
||||
};
|
||||
use serde::de::DeserializeOwned;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
use cw_storage_plus::{Item, Map, PrimaryKey};
|
||||
|
||||
// extracted into separate struct for easier cloning, access to mock structs, etc.
|
||||
// we also had to redefine the MockApi
|
||||
struct MockedDependencies {
|
||||
storage: MockStorage,
|
||||
api: MockApi,
|
||||
api: CW12MockApi,
|
||||
querier: MockQuerier,
|
||||
|
||||
// that's a bit annoying. We have to keep track of all balance changes for when we clone the state
|
||||
@@ -115,6 +122,65 @@ impl ContractState {
|
||||
self.deps().querier.query_all_balances(address)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
pub fn save_map_value<'a, K, T>(&mut self, map: &Map<'a, K, T>, k: K, data: &T) -> StdResult<()>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
K: PrimaryKey<'a>,
|
||||
{
|
||||
map.save(&mut self.deps.storage, k, data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
pub fn load_map_value<'a, K, T>(&self, map: &Map<'a, K, T>, k: K) -> StdResult<T>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
K: PrimaryKey<'a>,
|
||||
{
|
||||
map.load(&self.deps.storage, k)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
pub fn may_load_map_value<'a, K, T>(&self, map: &Map<'a, K, T>, k: K) -> StdResult<Option<T>>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
K: PrimaryKey<'a>,
|
||||
{
|
||||
map.may_load(&self.deps.storage, k)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
pub fn save_item<T>(&mut self, item: &Item<T>, data: &T) -> StdResult<()>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
{
|
||||
item.save(&mut self.deps.storage, data)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
pub fn load_item<T>(&self, item: &Item<T>) -> StdResult<T>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
{
|
||||
item.load(&self.deps.storage)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cw-storage-plus")]
|
||||
pub fn may_load_item<T>(&self, item: &Item<T>) -> StdResult<Option<T>>
|
||||
where
|
||||
T: Serialize + DeserializeOwned,
|
||||
{
|
||||
item.may_load(&self.deps.storage)
|
||||
}
|
||||
|
||||
pub fn read_key(&self, key: &[u8]) -> Option<Vec<u8>> {
|
||||
self.deps.storage.get(key)
|
||||
}
|
||||
|
||||
pub fn set_key_value(&mut self, key: &[u8], value: &[u8]) {
|
||||
self.deps.storage.set(key, value)
|
||||
}
|
||||
|
||||
pub fn deps(&self) -> Deps<'_> {
|
||||
Deps {
|
||||
storage: &self.deps.storage,
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::raw_msg_to_string;
|
||||
use cosmwasm_std::{Addr, BankMsg, Binary, Coin, Event};
|
||||
|
||||
fn format_coins(coins: &[Coin]) -> String {
|
||||
coins
|
||||
.iter()
|
||||
.map(|c| c.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
if coins.is_empty() {
|
||||
"<zero>".to_string()
|
||||
} else {
|
||||
coins
|
||||
.iter()
|
||||
.map(|c| c.to_string())
|
||||
.collect::<Vec<_>>()
|
||||
.join(",")
|
||||
}
|
||||
}
|
||||
|
||||
// specifically for tokens included in `Execute` that go into the contract
|
||||
@@ -75,12 +80,12 @@ impl FurtherExecution {
|
||||
}
|
||||
|
||||
pub fn pretty(&self) -> String {
|
||||
let msg_placeholder = "<PLACEHOLDER>";
|
||||
let msg = raw_msg_to_string(&self.msg);
|
||||
let total_funds = format_coins(&self.funds);
|
||||
|
||||
format!(
|
||||
"{} will be called with msg {} and {total_funds} funds",
|
||||
self.contract, msg_placeholder
|
||||
"{} will be called with msg {msg} and {total_funds} funds",
|
||||
self.contract
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -101,10 +106,12 @@ impl ExecutionStepResult {
|
||||
let events = format!("EVENTS: {:?}\n", self.events);
|
||||
out.push_str(&events);
|
||||
|
||||
if !self.incoming_tokens.is_empty() {
|
||||
if self.incoming_tokens.iter().any(|c| !c.amount.is_empty()) {
|
||||
out.push_str("MOVED TOKENS (CONTRACTS):\n");
|
||||
for incoming in &self.incoming_tokens {
|
||||
out.push_str(&format!("{}\n", incoming.pretty()))
|
||||
if !incoming.amount.is_empty() {
|
||||
out.push_str(&format!("{}\n", incoming.pretty()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
mod contract_mock;
|
||||
mod error;
|
||||
mod execution;
|
||||
mod mock_api;
|
||||
mod multi_contract_mock;
|
||||
mod raw_state;
|
||||
mod single_contract_mock;
|
||||
@@ -155,6 +156,14 @@ fn serialize_msg<M: Serialize>(msg: &M) -> StdResult<Binary> {
|
||||
cosmwasm_std::to_binary(msg)
|
||||
}
|
||||
|
||||
// used only for purposes of providing more informative error messages
|
||||
fn raw_msg_to_string(raw: &Binary) -> String {
|
||||
match serde_json::from_slice::<serde_json::Value>(raw.as_slice()) {
|
||||
Ok(deserialized) => deserialized.to_string(),
|
||||
Err(_) => "ERR: COULD NOT RECOVER THE ORIGINAL MESSAGE".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) mod sealed {
|
||||
use crate::{deserialize_msg, TestableContract};
|
||||
use cosmwasm_std::{Binary, Deps, DepsMut, Env, MessageInfo, QueryResponse, Response};
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
// unfortunately we have to redefine cosmwasm' MockApi,
|
||||
// as in cw1.0 they have set `CANONICAL_LENGTH` to 54 which makes
|
||||
// `addr_validate` of our existing contracts fail (say of 'n1nc5tatafv6eyq7llkr2gv50ff9e22mnf70qgjlv737ktmt4eswrq73f2nw')
|
||||
// this has changed in 1.2 but we can't use that version yet...
|
||||
|
||||
use cosmwasm_std::testing::{digit_sum, riffle_shuffle, MockApi};
|
||||
use cosmwasm_std::{
|
||||
Addr, Api, CanonicalAddr, RecoverPubkeyError, StdError, StdResult, VerificationError,
|
||||
};
|
||||
|
||||
const CANONICAL_LENGTH: usize = 90; // n = 45
|
||||
const SHUFFLES_ENCODE: usize = 10;
|
||||
const SHUFFLES_DECODE: usize = 2;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct CW12MockApi {
|
||||
inner: MockApi,
|
||||
canonical_length: usize,
|
||||
}
|
||||
|
||||
impl Default for CW12MockApi {
|
||||
fn default() -> Self {
|
||||
CW12MockApi {
|
||||
inner: MockApi::default(),
|
||||
canonical_length: CANONICAL_LENGTH,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// whatever we can, hand over to 1.0 MockApi
|
||||
impl Api for CW12MockApi {
|
||||
fn addr_validate(&self, input: &str) -> StdResult<Addr> {
|
||||
let canonical = self.addr_canonicalize(input)?;
|
||||
let normalized = self.addr_humanize(&canonical)?;
|
||||
if input != normalized {
|
||||
return Err(StdError::generic_err(
|
||||
"Invalid input: address not normalized",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(Addr::unchecked(input))
|
||||
}
|
||||
|
||||
fn addr_canonicalize(&self, input: &str) -> StdResult<CanonicalAddr> {
|
||||
// Dummy input validation. This is more sophisticated for formats like bech32, where format and checksum are validated.
|
||||
let min_length = 3;
|
||||
let max_length = self.canonical_length;
|
||||
if input.len() < min_length {
|
||||
return Err(StdError::generic_err(
|
||||
format!("Invalid input: human address too short for this mock implementation (must be >= {min_length})."),
|
||||
));
|
||||
}
|
||||
if input.len() > max_length {
|
||||
return Err(StdError::generic_err(
|
||||
format!("Invalid input: human address too long for this mock implementation (must be <= {max_length})."),
|
||||
));
|
||||
}
|
||||
|
||||
// mimicks formats like hex or bech32 where different casings are valid for one address
|
||||
let normalized = input.to_lowercase();
|
||||
|
||||
let mut out = Vec::from(normalized);
|
||||
|
||||
// pad to canonical length with NULL bytes
|
||||
out.resize(self.canonical_length, 0x00);
|
||||
// content-dependent rotate followed by shuffle to destroy
|
||||
// the most obvious structure (https://github.com/CosmWasm/cosmwasm/issues/552)
|
||||
let rotate_by = digit_sum(&out) % self.canonical_length;
|
||||
out.rotate_left(rotate_by);
|
||||
for _ in 0..SHUFFLES_ENCODE {
|
||||
out = riffle_shuffle(&out);
|
||||
}
|
||||
Ok(out.into())
|
||||
}
|
||||
|
||||
fn addr_humanize(&self, canonical: &CanonicalAddr) -> StdResult<Addr> {
|
||||
if canonical.len() != self.canonical_length {
|
||||
return Err(StdError::generic_err(
|
||||
"Invalid input: canonical address length not correct",
|
||||
));
|
||||
}
|
||||
|
||||
let mut tmp: Vec<u8> = canonical.clone().into();
|
||||
// Shuffle two more times which restored the original value (24 elements are back to original after 20 rounds)
|
||||
for _ in 0..SHUFFLES_DECODE {
|
||||
tmp = riffle_shuffle(&tmp);
|
||||
}
|
||||
// Rotate back
|
||||
let rotate_by = digit_sum(&tmp) % self.canonical_length;
|
||||
tmp.rotate_right(rotate_by);
|
||||
// Remove NULL bytes (i.e. the padding)
|
||||
let trimmed = tmp.into_iter().filter(|&x| x != 0x00).collect();
|
||||
// decode UTF-8 bytes into string
|
||||
let human = String::from_utf8(trimmed)?;
|
||||
Ok(Addr::unchecked(human))
|
||||
}
|
||||
|
||||
fn secp256k1_verify(
|
||||
&self,
|
||||
message_hash: &[u8],
|
||||
signature: &[u8],
|
||||
public_key: &[u8],
|
||||
) -> Result<bool, VerificationError> {
|
||||
self.inner
|
||||
.secp256k1_verify(message_hash, signature, public_key)
|
||||
}
|
||||
|
||||
fn secp256k1_recover_pubkey(
|
||||
&self,
|
||||
message_hash: &[u8],
|
||||
signature: &[u8],
|
||||
recovery_param: u8,
|
||||
) -> Result<Vec<u8>, RecoverPubkeyError> {
|
||||
self.inner
|
||||
.secp256k1_recover_pubkey(message_hash, signature, recovery_param)
|
||||
}
|
||||
|
||||
fn ed25519_verify(
|
||||
&self,
|
||||
message: &[u8],
|
||||
signature: &[u8],
|
||||
public_key: &[u8],
|
||||
) -> Result<bool, VerificationError> {
|
||||
self.inner.ed25519_verify(message, signature, public_key)
|
||||
}
|
||||
|
||||
fn ed25519_batch_verify(
|
||||
&self,
|
||||
messages: &[&[u8]],
|
||||
signatures: &[&[u8]],
|
||||
public_keys: &[&[u8]],
|
||||
) -> Result<bool, VerificationError> {
|
||||
self.inner
|
||||
.ed25519_batch_verify(messages, signatures, public_keys)
|
||||
}
|
||||
|
||||
fn debug(&self, message: &str) {
|
||||
self.inner.debug(message)
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ use crate::contract_mock::ContractState;
|
||||
use crate::execution::{
|
||||
CrossContractTokenMove, ExecutionResult, ExecutionStepResult, FurtherExecution,
|
||||
};
|
||||
use crate::{sealed, serialize_msg, test_rng, MockingError, TestableContract};
|
||||
use crate::{raw_msg_to_string, sealed, serialize_msg, test_rng, MockingError, TestableContract};
|
||||
use cosmwasm_std::testing::{mock_env, mock_info};
|
||||
use cosmwasm_std::{Addr, Binary, CosmosMsg, Env, MessageInfo, ReplyOn, Response, WasmMsg};
|
||||
use rand_chacha::rand_core::RngCore;
|
||||
@@ -183,6 +183,34 @@ impl MultiContractMock {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn contract_state(
|
||||
&self,
|
||||
contract_address: impl Into<String>,
|
||||
) -> Result<&ContractState, MockingError> {
|
||||
let addr = Addr::unchecked(contract_address.into());
|
||||
let contract =
|
||||
self.contracts
|
||||
.get(&addr)
|
||||
.ok_or_else(|| MockingError::NonExistentContract {
|
||||
address: addr.clone(),
|
||||
})?;
|
||||
Ok(&contract.state)
|
||||
}
|
||||
|
||||
pub fn contract_state_mut(
|
||||
&mut self,
|
||||
contract_address: impl Into<String>,
|
||||
) -> Result<&mut ContractState, MockingError> {
|
||||
let addr = Addr::unchecked(contract_address.into());
|
||||
let contract =
|
||||
self.contracts
|
||||
.get_mut(&addr)
|
||||
.ok_or_else(|| MockingError::NonExistentContract {
|
||||
address: addr.clone(),
|
||||
})?;
|
||||
Ok(&mut contract.state)
|
||||
}
|
||||
|
||||
// TODO: add support for sub msgs in instantiate response
|
||||
pub fn instantiate<C>(
|
||||
&mut self,
|
||||
@@ -332,14 +360,6 @@ impl MultiContractMock {
|
||||
}
|
||||
}
|
||||
|
||||
// used only for purposes of providing more informative error messages
|
||||
fn raw_msg_to_string(raw: &Binary) -> String {
|
||||
match serde_json::from_slice::<serde_json::Value>(raw.as_slice()) {
|
||||
Ok(deserialized) => deserialized.to_string(),
|
||||
Err(_) => "ERR: COULD NOT RECOVER THE ORIGINAL MESSAGE".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user