1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
//! Describes test environment API. Delegates methods to the underlying env implementation.
//!
//! Depending on the selected feature, the actual test env is dynamically loaded in the runtime or the Odra local MockVM is used.
use std::{collections::BTreeMap, panic::AssertUnwindSafe};

use odra_mock_vm_types::{
    Address, Balance, BlockTime, BorshDeserialize, Bytes, CallArgs, MockDeserializable,
    MockSerializable, PublicKey
};
use odra_types::{
    address::OdraAddress,
    event::{EventError, OdraEvent},
    OdraError
};

use crate::{native_token::NativeTokenMetadata, EntrypointArgs, EntrypointCall};

macro_rules! delegate_to_env {
    (
        $(
            $(#[$outer:meta])*
            fn $func_name:ident($( $param_ident:ident : $param_ty:ty ),*) $( -> $ret:ty)*
        )+
    ) => {
        $(
            $(#[$outer])*
            pub fn $func_name( $($param_ident : $param_ty),* ) $(-> $ret)* {
                crate::borrow_env().$func_name($($param_ident),*)
            }
        )+
    }
}

delegate_to_env! {
    /// Registers the contract in the test environment.
    fn register_contract(
        constructor: Option<(String, &CallArgs, EntrypointCall)>,
        constructors: BTreeMap<String, (EntrypointArgs, EntrypointCall)>,
        entrypoints: BTreeMap<String, (EntrypointArgs, EntrypointCall)>
    ) -> Address
    /// Increases the current value of block_time.
    fn advance_block_time_by(milliseconds: BlockTime)
    /// Returns the backend name.
    fn get_backend_name() -> String
    /// Replaces the current caller.
    fn set_caller(address: Address)
    /// Returns the balance of the account associated with the given address.
    fn token_balance(address: Address) -> Balance
    /// Returns nth test user account.
    fn get_account(n: usize) -> Address
}

/// Expects the `block` execution will fail with the specific error.
pub fn assert_exception<F, E>(err: E, block: F)
where
    F: FnOnce(),
    E: Into<OdraError>
{
    let _ = std::panic::catch_unwind(AssertUnwindSafe(|| {
        block();
    }));
    let exec_err = crate::borrow_env()
        .error()
        .expect("An error expected, but did not occur");

    assert_eq!(exec_err, err.into());
}

/// Returns the value that represents one native token.
pub fn one_token() -> Balance {
    Balance::one()
}

/// Calls contract at `address` invoking the `entrypoint` with `args`.
///
/// Returns optional raw bytes to further processing.
pub fn call_contract<T: MockSerializable + MockDeserializable>(
    address: Address,
    entrypoint: &str,
    args: &CallArgs,
    amount: Option<Balance>
) -> T {
    crate::borrow_env().call_contract(address, entrypoint, args, amount)
}

/// Gets nth event emitted by the contract at `address`.
pub fn get_event<T: MockSerializable + MockDeserializable + OdraEvent>(
    address: Address,
    index: i32
) -> Result<T, EventError> {
    let bytes = crate::borrow_env().get_event(address, index);

    bytes.and_then(|bytes| {
        let event_name = extract_event_name(&bytes)?;
        if event_name == T::name() {
            T::deser(bytes).map_err(|_| EventError::Parsing)
        } else {
            Err(EventError::UnexpectedType(event_name))
        }
    })
}

/// Returns the platform native token metadata
pub fn native_token_metadata() -> NativeTokenMetadata {
    NativeTokenMetadata::new()
}

/// Returns last call gas cost.
pub fn last_call_contract_gas_cost() -> Balance {
    Balance::zero()
}

/// Returns the amount of gas paid for last call.
pub fn last_call_contract_gas_used() -> Balance {
    Balance::zero()
}

/// Returns the total amount of gas used by the address.
/// Currently MockVM doesn't charge gas.
pub fn total_gas_used(address: Address) -> Balance {
    if address.is_contract() {
        panic!("Contract {:?} can't burn gas.", address)
    }
    Balance::zero()
}

/// Returns the report of entrypoints called, contract deployed and gas used.
/// Currently MockVM doesn't charge gas.
pub fn gas_report() -> Vec<(String, Balance)> {
    Vec::new()
}

/// Returns the name of the passed event
fn extract_event_name(mut bytes: &[u8]) -> Result<String, EventError> {
    let name = BorshDeserialize::deserialize(&mut bytes).map_err(|_| EventError::Formatting)?;
    Ok(name)
}

/// Signs the message using the private key associated with the given address.
pub fn sign_message(message: &Bytes, address: &Address) -> Bytes {
    let public_key = public_key(address);
    let mut message = message.inner_bytes().clone();
    message.extend_from_slice(public_key.inner_bytes());
    Bytes::from(message)
}

/// Returns the public key of the account associated with the given address.
pub fn public_key(address: &Address) -> PublicKey {
    PublicKey(*address)
}