near_sdk/test_utils/
context.rs

1use crate::mock::MockedBlockchain;
2use crate::test_utils::test_env::*;
3use crate::{test_vm_config, AccountId};
4use crate::{BlockHeight, EpochHeight, Gas, NearToken, PromiseResult, PublicKey, StorageUsage};
5use near_parameters::RuntimeFeesConfig;
6#[cfg(feature = "deterministic-account-ids")]
7use near_primitives_core::account::AccountContract;
8use near_primitives_core::config::ViewConfig;
9use std::convert::TryInto;
10use std::rc::Rc;
11
12/// Returns a pre-defined account_id from a list of 6.
13pub fn accounts(id: usize) -> AccountId {
14    ["alice", "bob", "charlie", "danny", "eugene", "fargo"][id].parse().unwrap()
15}
16
17/// Simple VMContext builder that allows to quickly create custom context in tests.
18#[derive(Clone)]
19pub struct VMContextBuilder {
20    pub context: VMContext,
21}
22
23impl Default for VMContextBuilder {
24    fn default() -> Self {
25        Self::new()
26    }
27}
28
29#[derive(Clone)]
30/// Context for the contract execution.
31pub struct VMContext {
32    /// The account id of the current contract that we are executing.
33    pub current_account_id: AccountId,
34    /// The account id of that signed the original transaction that led to this
35    /// execution.
36    pub signer_account_id: AccountId,
37    /// The public key that was used to sign the original transaction that led to
38    /// this execution.
39    pub signer_account_pk: PublicKey,
40    /// If this execution is the result of cross-contract call or a callback then
41    /// predecessor is the account that called it.
42    /// If this execution is the result of direct execution of transaction then it
43    /// is equal to `signer_account_id`.
44    pub predecessor_account_id: AccountId,
45    #[cfg(feature = "deterministic-account-ids")]
46    /// Where balance refunds after failure should go. Usually the same as
47    /// `predecessor_account_id` but may have been changed by the predecessor
48    /// via host function `promise_set_refund_to`.
49    pub refund_to_account_id: AccountId,
50    /// The input to the contract call.
51    /// Encoded as base64 string to be able to pass input in borsh binary format.
52    pub input: Rc<[u8]>,
53    /// The current block height.
54    pub block_index: BlockHeight,
55    /// The current block timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC).
56    pub block_timestamp: u64,
57    /// The current epoch height.
58    pub epoch_height: EpochHeight,
59
60    /// The balance attached to the given account. Excludes the `attached_deposit` that was
61    /// attached to the transaction.
62    pub account_balance: NearToken,
63    /// The balance of locked tokens on the given account.
64    pub account_locked_balance: NearToken,
65    /// The account's storage usage before the contract execution
66    pub storage_usage: StorageUsage,
67    #[cfg(feature = "deterministic-account-ids")]
68    /// The account's current contract code
69    pub account_contract: AccountContract,
70    /// The balance that was attached to the call that will be immediately deposited before the
71    /// contract execution starts.
72    pub attached_deposit: NearToken,
73    /// The gas attached to the call that can be used to pay for the gas fees.
74    pub prepaid_gas: Gas,
75    /// Initial seed for randomness
76    pub random_seed: [u8; 32],
77    /// If Some, it means that execution is made in a view mode and defines its configuration.
78    /// View mode means that only read-only operations are allowed.
79    /// See <https://nomicon.io/Proposals/view-change-method> for more details.
80    pub view_config: Option<ViewConfig>,
81    /// How many `DataReceipt`'s should receive this execution result. This should be empty if
82    /// this function call is a part of a batch and it is not the last action.
83    pub output_data_receivers: Vec<AccountId>,
84}
85
86impl VMContext {
87    pub fn is_view(&self) -> bool {
88        self.view_config.is_some()
89    }
90}
91
92#[allow(dead_code)]
93impl VMContextBuilder {
94    pub fn new() -> Self {
95        Self {
96            context: VMContext {
97                current_account_id: alice(),
98                signer_account_id: bob(),
99                signer_account_pk: vec![0u8; 33].try_into().unwrap(),
100                predecessor_account_id: bob(),
101                input: Rc::new([]),
102                block_index: 0,
103                block_timestamp: 0,
104                epoch_height: 0,
105                account_balance: NearToken::from_yoctonear(10u128.pow(26)),
106                account_locked_balance: NearToken::from_near(0),
107                storage_usage: 1024 * 300,
108                attached_deposit: NearToken::from_near(0),
109                prepaid_gas: Gas::from_tgas(300),
110                random_seed: [0u8; 32],
111                view_config: None,
112                output_data_receivers: vec![],
113                #[cfg(feature = "deterministic-account-ids")]
114                refund_to_account_id: bob(),
115                #[cfg(feature = "deterministic-account-ids")]
116                account_contract: AccountContract::None,
117            },
118        }
119    }
120
121    pub fn current_account_id(&mut self, account_id: AccountId) -> &mut Self {
122        self.context.current_account_id = account_id;
123        self
124    }
125
126    pub fn signer_account_id(&mut self, account_id: AccountId) -> &mut Self {
127        self.context.signer_account_id = account_id;
128        self
129    }
130
131    pub fn signer_account_pk(&mut self, pk: PublicKey) -> &mut Self {
132        self.context.signer_account_pk = pk;
133        self
134    }
135
136    pub fn predecessor_account_id(&mut self, account_id: AccountId) -> &mut Self {
137        self.context.predecessor_account_id = account_id;
138        self
139    }
140
141    #[deprecated(since = "4.1.2", note = "Use `block_height` method instead")]
142    pub fn block_index(&mut self, block_index: BlockHeight) -> &mut Self {
143        self.context.block_index = block_index;
144        self
145    }
146
147    pub fn block_height(&mut self, block_height: BlockHeight) -> &mut Self {
148        self.context.block_index = block_height;
149        self
150    }
151
152    pub fn block_timestamp(&mut self, block_timestamp: u64) -> &mut Self {
153        self.context.block_timestamp = block_timestamp;
154        self
155    }
156
157    pub fn epoch_height(&mut self, epoch_height: EpochHeight) -> &mut Self {
158        self.context.epoch_height = epoch_height;
159        self
160    }
161
162    pub fn account_balance(&mut self, amount: NearToken) -> &mut Self {
163        self.context.account_balance = amount;
164        self
165    }
166
167    pub fn account_locked_balance(&mut self, amount: NearToken) -> &mut Self {
168        self.context.account_locked_balance = amount;
169        self
170    }
171
172    pub fn storage_usage(&mut self, usage: StorageUsage) -> &mut Self {
173        self.context.storage_usage = usage;
174        self
175    }
176
177    pub fn attached_deposit(&mut self, amount: NearToken) -> &mut Self {
178        self.context.attached_deposit = amount;
179        self
180    }
181
182    pub fn prepaid_gas(&mut self, gas: Gas) -> &mut Self {
183        self.context.prepaid_gas = gas;
184        self
185    }
186
187    pub fn random_seed(&mut self, seed: [u8; 32]) -> &mut Self {
188        self.context.random_seed = seed;
189        self
190    }
191
192    #[cfg(feature = "deterministic-account-ids")]
193    pub fn refund_to_account_id(&mut self, beneficiary_id: AccountId) -> &mut Self {
194        self.context.refund_to_account_id = beneficiary_id;
195        self
196    }
197
198    #[cfg(feature = "deterministic-account-ids")]
199    pub fn account_contract(&mut self, contract: AccountContract) -> &mut Self {
200        self.context.account_contract = contract;
201        self
202    }
203
204    pub fn is_view(&mut self, is_view: bool) -> &mut Self {
205        self.context.view_config = if is_view {
206            Some(ViewConfig { max_gas_burnt: near_primitives::gas::Gas::from_teragas(200) })
207        } else {
208            None
209        };
210        self
211    }
212
213    pub fn build(&self) -> VMContext {
214        self.context.clone()
215    }
216}
217
218/// Initializes the [`MockedBlockchain`] with a single promise result during execution.
219#[deprecated(since = "4.0.0", note = "Use `testing_env!` macro to initialize with promise results")]
220pub fn testing_env_with_promise_results(context: VMContext, promise_result: PromiseResult) {
221    let storage = crate::mock::with_mocked_blockchain(|b| b.take_storage());
222
223    //? This probably shouldn't need to replace the existing mocked blockchain altogether?
224    //? Might be a good time to remove this utility function altogether
225    crate::env::set_blockchain_interface(MockedBlockchain::new(
226        context,
227        test_vm_config(),
228        RuntimeFeesConfig::test(),
229        vec![promise_result],
230        storage,
231        Default::default(),
232        None,
233    ));
234}