nitka/
context.rs

1use std::collections::HashMap;
2
3use anyhow::Result;
4use near_workspaces::{
5    network::{Sandbox, Testnet},
6    prelude::TopLevelAccountCreator,
7    types::NearToken,
8    Account, Contract, DevNetwork, Worker,
9};
10
11use crate::{build::build_contract, misc::load_wasm};
12
13const ONE_MINUTE_BLOCKS_HEIGHT: u64 = 240;
14
15#[derive(Debug)]
16pub struct Context<T> {
17    root_account: Account,
18    pub worker: Worker<T>,
19    pub accounts: HashMap<String, Account>,
20    pub contracts: HashMap<&'static str, Contract>,
21}
22
23impl Context<Sandbox> {
24    pub async fn new(contracts: &[&'static str], rebuild_contract: bool, make_command: Option<&str>) -> Result<Self> {
25        let worker = near_workspaces::sandbox().await?;
26        let root_account = worker.root_account()?;
27        Self::with_worker(contracts, root_account, worker, rebuild_contract, make_command).await
28    }
29}
30
31impl Context<Testnet> {
32    pub async fn new(contracts: &[&'static str], rebuild_contract: bool, make_command: Option<&str>) -> Result<Self> {
33        let worker = near_workspaces::testnet().await?;
34        let root_account = worker.dev_create_account().await?;
35        Self::with_worker(contracts, root_account, worker, rebuild_contract, make_command).await
36    }
37}
38
39impl<Network: DevNetwork + TopLevelAccountCreator + 'static> Context<Network> {
40    async fn with_worker(
41        contract_names: &[&'static str],
42        root_account: Account,
43        worker: Worker<Network>,
44        rebuild_contract: bool,
45        make_command: Option<&str>,
46    ) -> Result<Self> {
47        println!("🏭 Initializing context");
48
49        if rebuild_contract {
50            build_contract(make_command)?;
51        }
52
53        let mut context = Context {
54            root_account,
55            worker: worker.clone(),
56            accounts: HashMap::new(),
57            contracts: HashMap::new(),
58        };
59
60        let mut contracts = HashMap::<&'static str, Contract>::new();
61
62        for name in contract_names {
63            let account = context.account(name).await?;
64
65            let contract = account
66                .deploy(&load_wasm(&format!("../res/{name}.wasm")))
67                .await?
68                .into_result()?;
69
70            println!("📃 сontract {name} deployed to {}", contract.id());
71
72            contracts.insert(name, contract);
73        }
74
75        context.contracts = contracts;
76
77        Ok(context)
78    }
79
80    pub async fn account(&mut self, name: &str) -> Result<Account> {
81        self.account_with_balance(name, NearToken::from_near(5_000)).await
82    }
83
84    pub async fn account_with_balance(&mut self, name: &str, balance: NearToken) -> Result<Account> {
85        if !self.accounts.contains_key(name) {
86            let account = self
87                .root_account
88                .create_subaccount(name)
89                .initial_balance(balance)
90                .transact()
91                .await?
92                .into_result()?;
93
94            self.accounts.insert(name.to_string(), account);
95        }
96
97        Ok(self.accounts.get(name).unwrap().clone())
98    }
99}
100
101impl Context<Sandbox> {
102    pub async fn fast_forward_hours(&self, hours: u64) -> Result<()> {
103        self.fast_forward_minutes(hours * 60).await
104    }
105
106    pub async fn fast_forward_minutes(&self, minutes: u64) -> Result<()> {
107        let blocks_to_advance = ONE_MINUTE_BLOCKS_HEIGHT * minutes;
108        println!("⏳ Fast forward to {minutes} minutes ({blocks_to_advance} blocks)...");
109        self.worker.fast_forward(blocks_to_advance).await?;
110        Ok(())
111    }
112
113    pub async fn fast_forward_one_block(&self) -> Result<()> {
114        self.worker.fast_forward(1).await?;
115        Ok(())
116    }
117}