near_workspaces/network/
variants.rs

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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
use std::str::FromStr;

use crate::error::ErrorKind;
use crate::network::Info;
use crate::result::{Execution, Result};
use crate::rpc::client::Client;
use crate::types::{AccountId, KeyType, SecretKey};
use crate::{Account, Contract, Worker};
use async_trait::async_trait;

pub(crate) const DEV_ACCOUNT_SEED: &str = "testificate";

pub trait NetworkClient {
    fn client(&self) -> &Client;
}

pub trait NetworkInfo {
    fn info(&self) -> &Info;
}

/// Trait provides the ability to create a sponsored subaccount of network's root account.
///
/// Network's root account is identified by value returned by [`Self::root_account_id`] method.
/// The `subaccount_prefix` is a prefix for the subaccount ID.
/// For example, if this parameter is `"subaccount"` then
/// the full ID for testnet will be `"subaccount.testnet"`.
///
/// Currently all implementations validate that `subaccount_prefix` does not contain a `.`.
#[async_trait]
pub trait RootAccountSubaccountCreator {
    /// for sandbox value of [`Worker::<Sandbox>::root_account`]
    /// and for testnet value of [`Worker::<Testnet>::root_account_id`]
    /// are consistent with id that this method returns
    fn root_account_id(&self) -> Result<AccountId>;

    fn compute_subaccount_id(&self, subaccount_prefix: AccountId) -> Result<AccountId> {
        if subaccount_prefix.as_str().contains('.') {
            return Err(
                ErrorKind::Io.custom("Subaccount prefix for subaccount created cannot contain '.'")
            );
        }
        let root_id = self.root_account_id()?;
        AccountId::from_str(format!("{}.{}", subaccount_prefix, root_id).as_str())
            .map_err(|e| ErrorKind::DataConversion.custom(e))
    }

    async fn create_root_account_subaccount(
        &self,
        worker: Worker<dyn Network>,
        subaccount_prefix: AccountId,
        sk: SecretKey,
    ) -> Result<Execution<Account>>;

    async fn create_root_account_subaccount_and_deploy(
        &self,
        worker: Worker<dyn Network>,
        subaccount_prefix: AccountId,
        sk: SecretKey,
        wasm: &[u8],
    ) -> Result<Execution<Contract>>;
}

/// tla - stands for "top level account"
#[async_trait]
pub trait TopLevelAccountCreator {
    async fn create_tla(
        &self,
        worker: Worker<dyn Network>,
        id: AccountId,
        sk: SecretKey,
    ) -> Result<Execution<Account>>;

    async fn create_tla_and_deploy(
        &self,
        worker: Worker<dyn Network>,
        id: AccountId,
        sk: SecretKey,
        wasm: &[u8],
    ) -> Result<Execution<Contract>>;
}

impl<T> Worker<T> {
    pub fn generate_dev_account_credentials(&self) -> (AccountId, SecretKey) {
        let id = crate::rpc::tool::random_account_id();
        let sk = SecretKey::from_seed(KeyType::ED25519, DEV_ACCOUNT_SEED);
        (id, sk)
    }
}

impl<T> Worker<T>
where
    T: Network + TopLevelAccountCreator + 'static,
{
    /// Creates account `id` as top level account
    pub async fn create_tla(&self, id: AccountId, sk: SecretKey) -> Result<Execution<Account>> {
        let res = self
            .workspace
            .create_tla(self.clone().coerce(), id, sk)
            .await?;

        for callback in self.tx_callbacks.iter() {
            callback(res.details.total_gas_burnt)?;
        }

        Ok(res)
    }

    /// Creates account `id` as top level account and deploys wasm code to it
    pub async fn create_tla_and_deploy(
        &self,
        id: AccountId,
        sk: SecretKey,
        wasm: &[u8],
    ) -> Result<Execution<Contract>> {
        let res = self
            .workspace
            .create_tla_and_deploy(self.clone().coerce(), id, sk, wasm)
            .await?;

        for callback in self.tx_callbacks.iter() {
            callback(res.details.total_gas_burnt)?;
        }

        Ok(res)
    }

    /// Creates a top level developement account.
    /// On sandbox network it has a balance of 100 Near.
    /// If you need more Near for your tests in sandbox consider using [`Worker::<Sandbox>::root_account`] method.
    pub async fn dev_create_tla(&self) -> Result<Account> {
        let (id, sk) = self.generate_dev_account_credentials();
        let account = self.create_tla(id, sk).await?;
        Ok(account.into_result()?)
    }

    /// Creates a top level developement account and deploys wasm code to it.
    pub async fn dev_deploy_tla(&self, wasm: &[u8]) -> Result<Contract> {
        let (id, sk) = self.generate_dev_account_credentials();
        let contract = self.create_tla_and_deploy(id, sk, wasm).await?;
        Ok(contract.into_result()?)
    }
}

impl<T> Worker<T>
where
    T: DevNetwork + 'static,
{
    pub async fn create_root_account_subaccount(
        &self,
        subaccount_prefix: AccountId,
        sk: SecretKey,
    ) -> Result<Execution<Account>> {
        let res = self
            .workspace
            .create_root_account_subaccount(self.clone().coerce(), subaccount_prefix, sk)
            .await?;

        for callback in self.tx_callbacks.iter() {
            callback(res.details.total_gas_burnt)?;
        }

        Ok(res)
    }

    pub async fn create_root_account_subaccount_and_deploy(
        &self,
        subaccount_prefix: AccountId,
        sk: SecretKey,
        wasm: &[u8],
    ) -> Result<Execution<Contract>> {
        let res = self
            .workspace
            .create_root_account_subaccount_and_deploy(
                self.clone().coerce(),
                subaccount_prefix,
                sk,
                wasm,
            )
            .await?;

        for callback in self.tx_callbacks.iter() {
            callback(res.details.total_gas_burnt)?;
        }

        Ok(res)
    }

    /// Creates a subaccount of the network's [root account](RootAccountSubaccountCreator::root_account_id) with
    /// random account ID and secret key. By default, balance is around 10 Near for testnet
    /// and 100 NEAR for sandbox.
    pub async fn dev_create_account(&self) -> Result<Account> {
        let (id, sk) = self.generate_dev_account_credentials();
        let account = self.create_root_account_subaccount(id, sk).await?;
        Ok(account.into_result()?)
    }

    /// Creates a subaccount of the network's [root account](RootAccountSubaccountCreator::root_account_id) with
    /// random account ID and secret key and deploys provided wasm code into it.
    pub async fn dev_deploy(&self, wasm: &[u8]) -> Result<Contract> {
        let (id, sk) = self.generate_dev_account_credentials();
        let contract = self
            .create_root_account_subaccount_and_deploy(id, sk, wasm)
            .await?;
        Ok(contract.into_result()?)
    }
}

/// Network trait specifies the functionality of a network type such as mainnet, testnet or any
/// other networks that are not specified in this library.
pub trait Network: NetworkInfo + NetworkClient + Send + Sync {}

impl<T> Network for T where T: NetworkInfo + NetworkClient + Send + Sync {}

/// DevNetwork is a Network that can call into [`Worker::dev_create_account`] and [`Worker::dev_deploy`] to create developer accounts.
pub trait DevNetwork: Network + RootAccountSubaccountCreator + 'static {}

impl<T> DevNetwork for T where T: Network + RootAccountSubaccountCreator + 'static {}