near_workspaces/network/
variants.rs

1use std::str::FromStr;
2
3use crate::error::ErrorKind;
4use crate::network::Info;
5use crate::result::{Execution, Result};
6use crate::rpc::client::Client;
7use crate::types::{AccountId, KeyType, SecretKey};
8use crate::{Account, Contract, Worker};
9use async_trait::async_trait;
10
11pub(crate) const DEV_ACCOUNT_SEED: &str = "testificate";
12
13pub trait NetworkClient {
14    fn client(&self) -> &Client;
15}
16
17pub trait NetworkInfo {
18    fn info(&self) -> &Info;
19}
20
21/// Trait provides the ability to create a sponsored subaccount of network's root account.
22///
23/// Network's root account is identified by value returned by [`Self::root_account_id`] method.
24/// The `subaccount_prefix` is a prefix for the subaccount ID.
25/// For example, if this parameter is `"subaccount"` then
26/// the full ID for testnet will be `"subaccount.testnet"`.
27///
28/// Currently all implementations validate that `subaccount_prefix` does not contain a `.`.
29#[async_trait]
30pub trait RootAccountSubaccountCreator {
31    /// for sandbox value of [`Worker::<Sandbox>::root_account`]
32    /// and for testnet value of [`Worker::<Testnet>::root_account_id`]
33    /// are consistent with id that this method returns
34    fn root_account_id(&self) -> Result<AccountId>;
35
36    fn compute_subaccount_id(&self, subaccount_prefix: AccountId) -> Result<AccountId> {
37        if subaccount_prefix.as_str().contains('.') {
38            return Err(
39                ErrorKind::Io.custom("Subaccount prefix for subaccount created cannot contain '.'")
40            );
41        }
42        let root_id = self.root_account_id()?;
43        AccountId::from_str(format!("{subaccount_prefix}.{root_id}").as_str())
44            .map_err(|e| ErrorKind::DataConversion.custom(e))
45    }
46
47    async fn create_root_account_subaccount(
48        &self,
49        worker: Worker<dyn Network>,
50        subaccount_prefix: AccountId,
51        sk: SecretKey,
52    ) -> Result<Execution<Account>>;
53
54    async fn create_root_account_subaccount_and_deploy(
55        &self,
56        worker: Worker<dyn Network>,
57        subaccount_prefix: AccountId,
58        sk: SecretKey,
59        wasm: &[u8],
60    ) -> Result<Execution<Contract>>;
61}
62
63/// tla - stands for "top level account"
64#[async_trait]
65pub trait TopLevelAccountCreator {
66    async fn create_tla(
67        &self,
68        worker: Worker<dyn Network>,
69        id: AccountId,
70        sk: SecretKey,
71    ) -> Result<Execution<Account>>;
72
73    async fn create_tla_and_deploy(
74        &self,
75        worker: Worker<dyn Network>,
76        id: AccountId,
77        sk: SecretKey,
78        wasm: &[u8],
79    ) -> Result<Execution<Contract>>;
80}
81
82impl<T> Worker<T> {
83    pub fn generate_dev_account_credentials(&self) -> (AccountId, SecretKey) {
84        let id = crate::rpc::tool::random_account_id();
85        let sk = SecretKey::from_seed(KeyType::ED25519, id.as_str());
86        (id, sk)
87    }
88}
89
90impl<T> Worker<T>
91where
92    T: Network + TopLevelAccountCreator + 'static,
93{
94    /// Creates account `id` as top level account
95    pub async fn create_tla(&self, id: AccountId, sk: SecretKey) -> Result<Execution<Account>> {
96        let res = self
97            .workspace
98            .create_tla(self.clone().coerce(), id, sk)
99            .await?;
100
101        for callback in self.tx_callbacks.iter() {
102            callback(res.details.total_gas_burnt)?;
103        }
104
105        Ok(res)
106    }
107
108    /// Creates account `id` as top level account and deploys wasm code to it
109    pub async fn create_tla_and_deploy(
110        &self,
111        id: AccountId,
112        sk: SecretKey,
113        wasm: &[u8],
114    ) -> Result<Execution<Contract>> {
115        let res = self
116            .workspace
117            .create_tla_and_deploy(self.clone().coerce(), id, sk, wasm)
118            .await?;
119
120        for callback in self.tx_callbacks.iter() {
121            callback(res.details.total_gas_burnt)?;
122        }
123
124        Ok(res)
125    }
126
127    /// Creates a top level developement account.
128    /// On sandbox network it has a balance of 100 Near.
129    /// If you need more Near for your tests in sandbox consider using [`Worker::<Sandbox>::root_account`] method.
130    pub async fn dev_create_tla(&self) -> Result<Account> {
131        let (id, sk) = self.generate_dev_account_credentials();
132        let account = self.create_tla(id, sk).await?;
133        Ok(account.into_result()?)
134    }
135
136    /// Creates a top level developement account and deploys wasm code to it.
137    pub async fn dev_deploy_tla(&self, wasm: &[u8]) -> Result<Contract> {
138        let (id, sk) = self.generate_dev_account_credentials();
139        let contract = self.create_tla_and_deploy(id, sk, wasm).await?;
140        Ok(contract.into_result()?)
141    }
142}
143
144impl<T> Worker<T>
145where
146    T: DevNetwork + 'static,
147{
148    pub async fn create_root_account_subaccount(
149        &self,
150        subaccount_prefix: AccountId,
151        sk: SecretKey,
152    ) -> Result<Execution<Account>> {
153        let res = self
154            .workspace
155            .create_root_account_subaccount(self.clone().coerce(), subaccount_prefix, sk)
156            .await?;
157
158        for callback in self.tx_callbacks.iter() {
159            callback(res.details.total_gas_burnt)?;
160        }
161
162        Ok(res)
163    }
164
165    pub async fn create_root_account_subaccount_and_deploy(
166        &self,
167        subaccount_prefix: AccountId,
168        sk: SecretKey,
169        wasm: &[u8],
170    ) -> Result<Execution<Contract>> {
171        let res = self
172            .workspace
173            .create_root_account_subaccount_and_deploy(
174                self.clone().coerce(),
175                subaccount_prefix,
176                sk,
177                wasm,
178            )
179            .await?;
180
181        for callback in self.tx_callbacks.iter() {
182            callback(res.details.total_gas_burnt)?;
183        }
184
185        Ok(res)
186    }
187
188    /// Creates a subaccount of the network's [root account](RootAccountSubaccountCreator::root_account_id) with
189    /// random account ID and secret key. By default, balance is around 10 Near for testnet
190    /// and 100 NEAR for sandbox.
191    pub async fn dev_create_account(&self) -> Result<Account> {
192        let (id, sk) = self.generate_dev_account_credentials();
193        let account = self.create_root_account_subaccount(id, sk).await?;
194        Ok(account.into_result()?)
195    }
196
197    /// Creates a subaccount of the network's [root account](RootAccountSubaccountCreator::root_account_id) with
198    /// random account ID and secret key and deploys provided wasm code into it.
199    pub async fn dev_deploy(&self, wasm: &[u8]) -> Result<Contract> {
200        let (id, sk) = self.generate_dev_account_credentials();
201        let contract = self
202            .create_root_account_subaccount_and_deploy(id, sk, wasm)
203            .await?;
204        Ok(contract.into_result()?)
205    }
206}
207
208/// Network trait specifies the functionality of a network type such as mainnet, testnet or any
209/// other networks that are not specified in this library.
210pub trait Network: NetworkInfo + NetworkClient + Send + Sync {}
211
212impl<T> Network for T where T: NetworkInfo + NetworkClient + Send + Sync {}
213
214/// DevNetwork is a Network that can call into [`Worker::dev_create_account`] and [`Worker::dev_deploy`] to create developer accounts.
215pub trait DevNetwork: Network + RootAccountSubaccountCreator + 'static {}
216
217impl<T> DevNetwork for T where T: Network + RootAccountSubaccountCreator + 'static {}