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}