ink_env/engine/off_chain/
test_api.rs1use super::{
18 EnvInstance,
19 OnInstance,
20};
21use crate::{
22 Result,
23 types::Environment,
24};
25use core::fmt::Debug;
26use std::panic::UnwindSafe;
27
28pub use super::call_data::CallData;
29pub use ink_engine::ext::ChainSpec;
30use ink_primitives::{
31 AccountIdMapper,
32 Address,
33 H256,
34 U256,
35};
36
37#[derive(Clone)]
39pub struct EmittedEvent {
40 pub topics: Vec<[u8; 32]>,
42 pub data: Vec<u8>,
44}
45
46pub fn set_contract_balance(addr: Address, new_balance: U256) {
59 let min = ChainSpec::default().minimum_balance;
60 if new_balance < min && new_balance != U256::zero() {
61 panic!("Balance must be at least [{min}]. Use 0 as balance to reap the account.");
62 }
63
64 <EnvInstance as OnInstance>::on_instance(|instance| {
65 instance.engine.set_balance(addr, new_balance);
66 })
67}
68
69pub fn get_contract_balance<T>(addr: Address) -> Result<U256> {
80 <EnvInstance as OnInstance>::on_instance(|instance| {
81 instance.engine.get_balance(addr).map_err(Into::into)
82 })
83}
84
85pub fn set_clear_storage_disabled(_disable: bool) {
93 unimplemented!(
94 "off-chain environment does not yet support `set_clear_storage_disabled`"
95 );
96}
97
98pub fn advance_block<T>()
100where
101 T: Environment,
102{
103 <EnvInstance as OnInstance>::on_instance(|instance| {
104 instance.engine.advance_block();
105 })
106}
107
108pub fn set_caller(caller: Address) {
110 <EnvInstance as OnInstance>::on_instance(|instance| {
111 instance.engine.set_caller(caller);
112 })
113}
114
115pub fn set_callee(callee: Address) {
117 <EnvInstance as OnInstance>::on_instance(|instance| {
118 instance.engine.set_callee(callee);
119 })
120}
121
122pub fn set_contract(contract: Address) {
124 <EnvInstance as OnInstance>::on_instance(|instance| {
125 instance.engine.set_contract(contract);
126 })
127}
128
129pub fn is_contract(contract: Address) -> bool {
131 <EnvInstance as OnInstance>::on_instance(|instance| {
132 instance.engine.is_contract(&contract)
133 })
134}
135
136pub fn callee() -> Address {
140 <EnvInstance as OnInstance>::on_instance(|instance| {
141 let callee = instance.engine.get_callee();
142 scale::Decode::decode(&mut &callee[..])
143 .unwrap_or_else(|err| panic!("encoding failed: {err}"))
144 })
145}
146
147pub fn get_contract_storage_rw(addr: Address) -> (usize, usize) {
149 <EnvInstance as OnInstance>::on_instance(|instance| {
150 instance.engine.get_contract_storage_rw(addr)
151 })
152}
153
154pub fn set_value_transferred(value: U256) {
159 <EnvInstance as OnInstance>::on_instance(|instance| {
160 instance.engine.set_value_transferred(value);
161 })
162}
163
164#[allow(clippy::arithmetic_side_effects)] pub fn transfer_in(value: U256) {
170 <EnvInstance as OnInstance>::on_instance(|instance| {
171 let caller = instance.engine.exec_context.caller;
172
173 let caller_old_balance = instance.engine.get_balance(caller).unwrap_or_default();
174
175 let callee = instance.engine.get_callee();
176 let contract_old_balance =
177 instance.engine.get_balance(callee).unwrap_or_default();
178
179 instance
180 .engine
181 .set_balance(caller, caller_old_balance - value);
182 instance
183 .engine
184 .set_balance(callee, contract_old_balance + value);
185 instance.engine.set_value_transferred(value);
186 });
187}
188
189pub fn count_used_storage_cells<T>(addr: Address) -> Result<usize>
193where
194 T: Environment,
195{
196 <EnvInstance as OnInstance>::on_instance(|instance| {
197 instance
198 .engine
199 .count_used_storage_cells(&addr)
200 .map_err(Into::into)
201 })
202}
203
204pub fn set_block_timestamp<T>(value: T::Timestamp)
206where
207 T: Environment<Timestamp = u64>,
208{
209 <EnvInstance as OnInstance>::on_instance(|instance| {
210 instance.engine.set_block_timestamp(value);
211 })
212}
213
214pub fn set_block_number<T>(value: T::BlockNumber)
216where
217 T: Environment<BlockNumber = u32>,
218{
219 <EnvInstance as OnInstance>::on_instance(|instance| {
220 instance.engine.set_block_number(value);
221 })
222}
223
224pub fn run_test<T, F>(f: F) -> Result<()>
227where
228 T: Environment,
229 F: FnOnce(DefaultAccounts) -> Result<()>,
230{
231 let default_accounts = default_accounts();
232 <EnvInstance as OnInstance>::on_instance(|instance| {
233 instance.engine.initialize_or_reset();
234
235 let alice = default_accounts.alice;
236 instance.engine.set_callee(alice);
238
239 let substantial = 1_000_000.into();
241 let some = 1_000.into();
242 instance.engine.set_balance(alice, substantial);
243 instance.engine.set_balance(default_accounts.bob, some);
244 instance.engine.set_balance(default_accounts.charlie, some);
245 instance
246 .engine
247 .set_balance(default_accounts.django, 0.into());
248 instance.engine.set_balance(default_accounts.eve, 0.into());
249 instance
250 .engine
251 .set_balance(default_accounts.frank, 0.into());
252 });
253 f(default_accounts)
254}
255
256pub fn default_accounts() -> DefaultAccounts {
259 DefaultAccounts {
260 alice: AccountIdMapper::to_address(&[0x01; 32]),
261 bob: AccountIdMapper::to_address(&[0x02; 32]),
262 charlie: AccountIdMapper::to_address(&[0x03; 32]),
263 django: AccountIdMapper::to_address(&[0x04; 32]),
264 eve: AccountIdMapper::to_address(&[0x05; 32]),
265 frank: AccountIdMapper::to_address(&[0x06; 32]),
266 }
267}
268
269pub struct DefaultAccounts {
271 pub alice: Address,
273 pub bob: Address,
275 pub charlie: Address,
277 pub django: Address,
279 pub eve: Address,
281 pub frank: Address,
283}
284
285pub fn recorded_events() -> Vec<EmittedEvent> {
287 <EnvInstance as OnInstance>::on_instance(|instance| {
288 instance
289 .engine
290 .get_emitted_events()
291 .map(|evt: ink_engine::test_api::EmittedEvent| evt.into())
292 .collect()
293 })
294}
295
296pub fn assert_contract_termination<T, F>(
321 should_terminate: F,
322 expected_beneficiary: Address,
323 expected_value_transferred_to_beneficiary: U256,
324) where
325 T: Environment,
326 F: FnMut() + UnwindSafe,
327 <T as Environment>::AccountId: Debug,
328 <T as Environment>::Balance: Debug,
329{
330 let value_any = ::std::panic::catch_unwind(should_terminate)
331 .expect_err("contract did not terminate");
332 let encoded_input = value_any
333 .downcast_ref::<Vec<u8>>()
334 .expect("panic object can not be cast");
335 let (value_transferred, beneficiary): (U256, Address) =
336 scale::Decode::decode(&mut &encoded_input[..])
337 .unwrap_or_else(|err| panic!("input can not be decoded: {err}"));
338 assert_eq!(value_transferred, expected_value_transferred_to_beneficiary);
339 assert_eq!(beneficiary, expected_beneficiary);
340}
341
342#[macro_export]
345macro_rules! pay_with_call {
346 ($contract:ident . $message:ident ( $( $params:expr ),* ) , $amount:expr) => {{
347 $crate::test::transfer_in($amount);
348 $contract.$message($ ($params) ,*)
349 }}
350}
351
352pub fn get_return_value() -> Vec<u8> {
354 <EnvInstance as OnInstance>::on_instance(|instance| instance.get_return_value())
355}
356
357pub fn upload_code<E, ContractRef>() -> H256
359where
360 E: Environment,
361 ContractRef: crate::ContractReverseReference,
362 <ContractRef as crate::ContractReverseReference>::Type:
363 crate::reflect::ContractMessageDecoder,
364{
365 <EnvInstance as OnInstance>::on_instance(|instance| {
366 instance.upload_code::<ContractRef>()
367 })
368}