near_workspaces/network/
testnet.rs

1use std::path::PathBuf;
2use std::str::FromStr;
3
4use async_trait::async_trait;
5use near_gas::NearGas;
6use url::Url;
7
8use near_primitives::views::ExecutionStatusView;
9
10use crate::network::builder::{FromNetworkBuilder, NetworkBuilder};
11use crate::network::Info;
12use crate::network::{NetworkClient, NetworkInfo, RootAccountSubaccountCreator};
13use crate::result::{Execution, ExecutionDetails, ExecutionFinalResult, ExecutionOutcome, Result};
14use crate::rpc::{client::Client, tool};
15use crate::types::{AccountId, InMemorySigner, NearToken, SecretKey};
16use crate::{Account, Contract, CryptoHash, Network, Worker};
17
18/// URL to the testnet RPC node provided by near.org.
19pub const RPC_URL: &str = "https://rpc.testnet.near.org";
20
21/// URL to the helper contract used to create named accounts provided by near.org.
22pub const HELPER_URL: &str = "https://helper.testnet.near.org";
23
24/// URL to the testnet archival RPC node provided by near.org.
25pub const ARCHIVAL_URL: &str = "https://archival-rpc.testnet.near.org";
26
27/// Testnet related configuration for interacting with testnet.
28///
29/// Look at [`workspaces::testnet`] and [`workspaces::testnet_archival`] for how
30/// to spin up a [`Worker`] that can be used to run tests in testnet.
31///
32/// [`workspaces::testnet`]: crate::testnet
33/// [`workspaces::testnet_archival`]: crate::testnet_archival
34/// [`Worker`]: crate::Worker
35pub struct Testnet {
36    client: Client,
37    info: Info,
38}
39
40#[async_trait]
41impl FromNetworkBuilder for Testnet {
42    async fn from_builder<'a>(build: NetworkBuilder<'a, Self>) -> Result<Self> {
43        let rpc_url = build.rpc_addr.unwrap_or_else(|| RPC_URL.into());
44        let client = Client::new(&rpc_url, build.api_key)?;
45        client.wait_for_rpc().await?;
46
47        Ok(Self {
48            client,
49            info: Info {
50                name: build.name.into(),
51                root_id: AccountId::from_str("testnet").unwrap(),
52                keystore_path: PathBuf::from(".near-credentials/testnet/"),
53                rpc_url: Url::parse(&rpc_url).expect("url is hardcoded"),
54            },
55        })
56    }
57}
58
59impl std::fmt::Debug for Testnet {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        f.debug_struct("Testnet")
62            .field("root_id", &self.info.root_id)
63            .field("rpc_url", &self.info.rpc_url)
64            .finish()
65    }
66}
67
68#[async_trait]
69impl RootAccountSubaccountCreator for Testnet {
70    fn root_account_id(&self) -> Result<AccountId> {
71        Ok(self.info().root_id.clone())
72    }
73
74    async fn create_root_account_subaccount(
75        &self,
76        worker: Worker<dyn Network>,
77        subaccount_prefix: AccountId,
78        sk: SecretKey,
79        // TODO: return Account only, but then you don't get metadata info for it...
80    ) -> Result<Execution<Account>> {
81        let id = self.compute_subaccount_id(subaccount_prefix)?;
82        let url = Url::parse(HELPER_URL).unwrap();
83        //only registrar can create tla on testnet, so must concatenate random created id with .testnet
84        tool::url_create_account(url, id.clone(), sk.public_key()).await?;
85        let signer = InMemorySigner::from_secret_key(id, sk);
86
87        Ok(Execution {
88            result: Account::new(signer, worker),
89            details: ExecutionFinalResult {
90                // We technically have not burnt any gas ourselves since someone else paid to
91                // create the account for us in testnet when we used the Helper contract.
92                total_gas_burnt: NearGas::from_gas(0),
93
94                status: near_primitives::views::FinalExecutionStatus::SuccessValue(Vec::new()),
95                details: ExecutionDetails {
96                    transaction: ExecutionOutcome {
97                        transaction_hash: CryptoHash::default(),
98                        block_hash: CryptoHash::default(),
99                        logs: Vec::new(),
100                        receipt_ids: Vec::new(),
101                        gas_burnt: NearGas::from_gas(0),
102                        tokens_burnt: NearToken::from_near(0),
103                        executor_id: "testnet".parse().unwrap(),
104                        status: ExecutionStatusView::SuccessValue(Vec::new()),
105                    },
106                    receipts: Vec::new(),
107                },
108            },
109        })
110    }
111
112    async fn create_root_account_subaccount_and_deploy(
113        &self,
114        worker: Worker<dyn Network>,
115        subaccount_prefix: AccountId,
116        sk: SecretKey,
117        wasm: &[u8],
118    ) -> Result<Execution<Contract>> {
119        let account = self
120            .create_root_account_subaccount(worker, subaccount_prefix.clone(), sk)
121            .await?;
122        let account = account.into_result()?;
123
124        account.deploy(wasm).await
125    }
126}
127
128impl NetworkClient for Testnet {
129    fn client(&self) -> &Client {
130        &self.client
131    }
132}
133
134impl NetworkInfo for Testnet {
135    fn info(&self) -> &Info {
136        &self.info
137    }
138}