1use cosmwasm_std::Coin;
5use std::collections::HashSet;
6
7use crate::capabilities::capabilities_from_csv;
8use crate::compatibility::check_wasm;
9use crate::instance::{Instance, InstanceOptions};
10use crate::internals::Logger;
11use crate::size::Size;
12use crate::{Backend, BackendApi, Querier, Storage, WasmLimits};
13
14use super::mock::{MockApi, MOCK_CONTRACT_ADDR};
15use super::querier::MockQuerier;
16use super::storage::MockStorage;
17
18const DEFAULT_GAS_LIMIT: u64 = 2_000_000_000; const DEFAULT_MEMORY_LIMIT: Option<Size> = Some(Size::mebi(16));
23
24pub fn mock_instance(
25 wasm: &[u8],
26 contract_balance: &[Coin],
27) -> Instance<MockApi, MockStorage, MockQuerier> {
28 mock_instance_with_options(
29 wasm,
30 MockInstanceOptions {
31 contract_balance: Some(contract_balance),
32 ..Default::default()
33 },
34 )
35}
36
37pub fn mock_instance_with_failing_api(
38 wasm: &[u8],
39 contract_balance: &[Coin],
40 backend_error: &'static str,
41) -> Instance<MockApi, MockStorage, MockQuerier> {
42 mock_instance_with_options(
43 wasm,
44 MockInstanceOptions {
45 contract_balance: Some(contract_balance),
46 backend_error: Some(backend_error),
47 ..Default::default()
48 },
49 )
50}
51
52pub fn mock_instance_with_balances(
53 wasm: &[u8],
54 balances: &[(&str, &[Coin])],
55) -> Instance<MockApi, MockStorage, MockQuerier> {
56 mock_instance_with_options(
57 wasm,
58 MockInstanceOptions {
59 balances,
60 ..Default::default()
61 },
62 )
63}
64
65pub fn mock_instance_with_gas_limit(
68 wasm: &[u8],
69 gas_limit: u64,
70) -> Instance<MockApi, MockStorage, MockQuerier> {
71 mock_instance_with_options(
72 wasm,
73 MockInstanceOptions {
74 gas_limit,
75 ..Default::default()
76 },
77 )
78}
79
80#[derive(Debug)]
81pub struct MockInstanceOptions<'a> {
82 pub balances: &'a [(&'a str, &'a [Coin])],
84 pub contract_balance: Option<&'a [Coin]>,
86 pub backend_error: Option<&'static str>,
88
89 pub available_capabilities: HashSet<String>,
91 pub gas_limit: u64,
93 pub memory_limit: Option<Size>,
95}
96
97impl MockInstanceOptions<'_> {
98 fn default_capabilities() -> HashSet<String> {
99 #[allow(unused_mut)]
100 let mut out = capabilities_from_csv(
101 "ibc2,iterator,staking,cosmwasm_1_1,cosmwasm_1_2,cosmwasm_1_3,cosmwasm_1_4,cosmwasm_2_0,cosmwasm_2_1,cosmwasm_2_2,cosmwasm_3_0",
102 );
103 #[cfg(feature = "stargate")]
104 out.insert("stargate".to_string());
105 out
106 }
107}
108
109impl Default for MockInstanceOptions<'_> {
110 fn default() -> Self {
111 Self {
112 balances: Default::default(),
114 contract_balance: Default::default(),
115 backend_error: None,
116
117 available_capabilities: Self::default_capabilities(),
119 gas_limit: DEFAULT_GAS_LIMIT,
120 memory_limit: DEFAULT_MEMORY_LIMIT,
121 }
122 }
123}
124
125pub fn mock_instance_with_options(
126 wasm: &[u8],
127 options: MockInstanceOptions,
128) -> Instance<MockApi, MockStorage, MockQuerier> {
129 check_wasm(
130 wasm,
131 &options.available_capabilities,
132 &WasmLimits::default(),
133 Logger::Off,
134 )
135 .unwrap();
136 let contract_address = MOCK_CONTRACT_ADDR;
137
138 let mut balances = options.balances.to_vec();
140 if let Some(contract_balance) = options.contract_balance {
141 if let Some(pos) = balances.iter().position(|item| item.0 == contract_address) {
143 balances.remove(pos);
144 }
145 balances.push((contract_address, contract_balance));
146 }
147
148 let api = if let Some(backend_error) = options.backend_error {
149 MockApi::new_failing(backend_error)
150 } else {
151 MockApi::default()
152 };
153
154 let backend = Backend {
155 api,
156 storage: MockStorage::default(),
157 querier: MockQuerier::new(&balances),
158 };
159 let memory_limit = options.memory_limit;
160 let options = InstanceOptions {
161 gas_limit: options.gas_limit,
162 };
163 Instance::from_code(wasm, backend, options, memory_limit).unwrap()
164}
165
166pub fn mock_instance_options() -> (InstanceOptions, Option<Size>) {
168 (
169 InstanceOptions {
170 gas_limit: DEFAULT_GAS_LIMIT,
171 },
172 DEFAULT_MEMORY_LIMIT,
173 )
174}
175
176pub fn test_io<A, S, Q>(instance: &mut Instance<A, S, Q>)
179where
180 A: BackendApi + 'static,
181 S: Storage + 'static,
182 Q: Querier + 'static,
183{
184 let sizes: Vec<usize> = vec![0, 1, 3, 10, 200, 2000, 5 * 1024];
185 let bytes: Vec<u8> = vec![0x00, 0xA5, 0xFF];
186
187 for size in sizes.into_iter() {
188 for byte in bytes.iter() {
189 let original = vec![*byte; size];
190 let wasm_ptr = instance
191 .allocate(original.len())
192 .expect("Could not allocate memory");
193 instance
194 .write_memory(wasm_ptr, &original)
195 .expect("Could not write data");
196 let wasm_data = instance.read_memory(wasm_ptr, size).expect("error reading");
197 assert_eq!(
198 original, wasm_data,
199 "failed for size {size}; expected: {original:?}; actual: {wasm_data:?}"
200 );
201 instance
202 .deallocate(wasm_ptr)
203 .expect("Could not deallocate memory");
204 }
205 }
206}