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_account_balance(addr: Address, new_balance: U256) {
63 let min = ChainSpec::default().minimum_balance;
64 if new_balance < min && new_balance != U256::zero() {
65 panic!("Balance must be at least [{min}]. Use 0 as balance to reap the account.");
66 }
67
68 <EnvInstance as OnInstance>::on_instance(|instance| {
69 instance.engine.set_balance(addr, new_balance);
70 })
71}
72
73pub fn get_account_balance<T>(addr: Address) -> Result<U256> {
86 <EnvInstance as OnInstance>::on_instance(|instance| {
87 instance.engine.get_balance(addr).map_err(Into::into)
88 })
89}
90
91pub fn set_clear_storage_disabled(_disable: bool) {
99 unimplemented!(
100 "off-chain environment does not yet support `set_clear_storage_disabled`"
101 );
102}
103
104pub fn advance_block<T>()
106where
107 T: Environment,
108{
109 <EnvInstance as OnInstance>::on_instance(|instance| {
110 instance.engine.advance_block();
111 })
112}
113
114pub fn set_caller(caller: Address) {
116 <EnvInstance as OnInstance>::on_instance(|instance| {
117 instance.engine.set_caller(caller);
118 })
119}
120
121pub fn set_callee(callee: Address) {
123 <EnvInstance as OnInstance>::on_instance(|instance| {
124 instance.engine.set_callee(callee);
125 })
126}
127
128pub fn set_contract(contract: Address) {
130 <EnvInstance as OnInstance>::on_instance(|instance| {
131 instance.engine.set_contract(contract);
132 })
133}
134
135#[cfg(feature = "unstable-hostfn")]
137pub fn is_contract(contract: Address) -> bool {
138 <EnvInstance as OnInstance>::on_instance(|instance| {
139 instance.engine.is_contract(&contract)
140 })
141}
142
143pub fn callee() -> Address {
147 <EnvInstance as OnInstance>::on_instance(|instance| {
148 let callee = instance.engine.get_callee();
149 scale::Decode::decode(&mut &callee[..])
150 .unwrap_or_else(|err| panic!("encoding failed: {err}"))
151 })
152}
153
154pub fn get_contract_storage_rw(addr: Address) -> (usize, usize) {
156 <EnvInstance as OnInstance>::on_instance(|instance| {
157 instance.engine.get_contract_storage_rw(addr)
158 })
159}
160
161pub fn set_value_transferred(value: U256) {
166 <EnvInstance as OnInstance>::on_instance(|instance| {
167 instance.engine.set_value_transferred(value);
168 })
169}
170
171#[allow(clippy::arithmetic_side_effects)] pub fn transfer_in(value: U256) {
177 <EnvInstance as OnInstance>::on_instance(|instance| {
178 let caller = instance.engine.exec_context.caller;
179
180 let caller_old_balance = instance.engine.get_balance(caller).unwrap_or_default();
181
182 let callee = instance.engine.get_callee();
183 let contract_old_balance =
184 instance.engine.get_balance(callee).unwrap_or_default();
185
186 instance
187 .engine
188 .set_balance(caller, caller_old_balance - value);
189 instance
190 .engine
191 .set_balance(callee, contract_old_balance + value);
192 instance.engine.set_value_transferred(value);
193 });
194}
195
196pub fn count_used_storage_cells<T>(addr: Address) -> Result<usize>
200where
201 T: Environment,
202{
203 <EnvInstance as OnInstance>::on_instance(|instance| {
204 instance
205 .engine
206 .count_used_storage_cells(&addr)
207 .map_err(Into::into)
208 })
209}
210
211pub fn set_block_timestamp<T>(value: T::Timestamp)
213where
214 T: Environment<Timestamp = u64>,
215{
216 <EnvInstance as OnInstance>::on_instance(|instance| {
217 instance.engine.set_block_timestamp(value);
218 })
219}
220
221pub fn set_block_number<T>(value: T::BlockNumber)
223where
224 T: Environment<BlockNumber = u32>,
225{
226 <EnvInstance as OnInstance>::on_instance(|instance| {
227 instance.engine.set_block_number(value);
228 })
229}
230
231pub fn run_test<T, F>(f: F) -> Result<()>
234where
235 T: Environment,
236 F: FnOnce(DefaultAccounts) -> Result<()>,
237{
238 let default_accounts = default_accounts();
239 <EnvInstance as OnInstance>::on_instance(|instance| {
240 instance.engine.initialize_or_reset();
241
242 let alice = default_accounts.alice;
243 instance.engine.set_callee(alice);
245
246 let substantial = 1_000_000.into();
248 let some = 1_000.into();
249 instance.engine.set_balance(alice, substantial);
250 instance.engine.set_balance(default_accounts.bob, some);
251 instance.engine.set_balance(default_accounts.charlie, some);
252 instance
253 .engine
254 .set_balance(default_accounts.django, 0.into());
255 instance.engine.set_balance(default_accounts.eve, 0.into());
256 instance
257 .engine
258 .set_balance(default_accounts.frank, 0.into());
259 });
260 f(default_accounts)
261}
262
263pub fn default_accounts() -> DefaultAccounts {
267 DefaultAccounts {
268 alice: AccountIdMapper::to_address(&[0x01; 32]),
269 bob: AccountIdMapper::to_address(&[0x02; 32]),
270 charlie: AccountIdMapper::to_address(&[0x03; 32]),
271 django: AccountIdMapper::to_address(&[0x04; 32]),
272 eve: AccountIdMapper::to_address(&[0x05; 32]),
273 frank: AccountIdMapper::to_address(&[0x06; 32]),
274 }
275}
276
277pub struct DefaultAccounts {
279 pub alice: Address,
281 pub bob: Address,
283 pub charlie: Address,
285 pub django: Address,
287 pub eve: Address,
289 pub frank: Address,
291}
292
293pub fn recorded_events() -> Vec<EmittedEvent> {
295 <EnvInstance as OnInstance>::on_instance(|instance| {
296 instance
297 .engine
298 .get_emitted_events()
299 .map(|evt: ink_engine::test_api::EmittedEvent| evt.into())
300 .collect()
301 })
302}
303
304pub fn assert_contract_termination<T, F>(
329 should_terminate: F,
330 expected_beneficiary: Address,
331 expected_value_transferred_to_beneficiary: U256,
332) where
333 T: Environment,
334 F: FnMut() + UnwindSafe,
335 <T as Environment>::AccountId: Debug,
336 <T as Environment>::Balance: Debug,
337{
338 let value_any = ::std::panic::catch_unwind(should_terminate)
339 .expect_err("contract did not terminate");
340 let encoded_input = value_any
341 .downcast_ref::<Vec<u8>>()
342 .expect("panic object can not be cast");
343 let (value_transferred, beneficiary): (U256, Address) =
344 scale::Decode::decode(&mut &encoded_input[..])
345 .unwrap_or_else(|err| panic!("input can not be decoded: {err}"));
346 assert_eq!(value_transferred, expected_value_transferred_to_beneficiary);
347 assert_eq!(beneficiary, expected_beneficiary);
348}
349
350#[macro_export]
353macro_rules! pay_with_call {
354 ($contract:ident . $message:ident ( $( $params:expr ),* ) , $amount:expr) => {{
355 $crate::test::transfer_in($amount);
356 $contract.$message($ ($params) ,*)
357 }}
358}
359
360pub fn get_return_value() -> Vec<u8> {
362 <EnvInstance as OnInstance>::on_instance(|instance| instance.get_return_value())
363}
364
365pub fn upload_code<E, ContractRef>() -> H256
367where
368 E: Environment,
369 ContractRef: crate::ContractReverseReference,
370 <ContractRef as crate::ContractReverseReference>::Type:
371 crate::reflect::ContractMessageDecoder,
372{
373 <EnvInstance as OnInstance>::on_instance(|instance| {
374 instance.upload_code::<ContractRef>()
375 })
376}