Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458
  1. use near_rpc::{
  2. client::{NearClient, Signer},
  3. utils::{ViewAccessKey, ViewAccessKeyResult},
  4. };
  5. use std::{
  6. fs::{create_dir_all, write},
  7. str::FromStr,
  8. };
  9. use common_api::crypto::prelude::*;
  10. use near_primitives_light::{types::Finality, views::AccessKeyView};
  11. use rand::{RngCore, SeedableRng};
  12. use rand_chacha::ChaChaRng;
  13. use reqwest::Url;
  14. use serde_json::json;
  15. use workspaces::{network::Sandbox, types::SecretKey, AccountId, Worker};
  16. // auxiliary structs and methods
  17. fn near_client(worker: &Worker<Sandbox>) -> NearClient {
  18. let rpc_url = Url::parse(format!("http://localhost:{}", worker.rpc_port()).as_str()).unwrap();
  19. NearClient::new(rpc_url).unwrap()
  20. }
  21. async fn create_signer(
  22. worker: &Worker<Sandbox>,
  23. client: &NearClient,
  24. signer_acc_id: &AccountId,
  25. ) -> Signer {
  26. let sk = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  27. let pk = Ed25519PublicKey::from(&sk);
  28. let keypair = Keypair::new(sk).to_string();
  29. let workspaces_sk = SecretKey::from_str(&keypair).unwrap();
  30. let _ = worker
  31. .create_tla(signer_acc_id.clone(), workspaces_sk)
  32. .await
  33. .unwrap();
  34. let view_access_key = client
  35. .view_access_key(signer_acc_id, &pk, Finality::None)
  36. .await
  37. .unwrap();
  38. match view_access_key.result {
  39. ViewAccessKeyResult::Ok(AccessKeyView { nonce, .. }) => {
  40. Signer::from_secret_str(&keypair, signer_acc_id.clone(), nonce).unwrap()
  41. }
  42. ViewAccessKeyResult::Err { error, .. } => panic!("{error}"),
  43. }
  44. }
  45. async fn download_contract() -> Vec<u8> {
  46. let target = "https://github.com/near-examples/FT/raw/master/res/fungible_token.wasm";
  47. let target_path = temp_dir().into_path();
  48. let fname = "contract.wasm";
  49. let full_dest = format!("{}/{}", target_path.to_string_lossy(), fname);
  50. let contract_bytes = reqwest::get(target).await.unwrap().bytes().await.unwrap();
  51. write(full_dest, &contract_bytes).unwrap();
  52. contract_bytes.to_vec()
  53. }
  54. async fn clone_and_compile_wasm() -> Vec<u8> {
  55. let tmp_path = temp_dir().into_path();
  56. let repo_path = format!("{}/near-smartcontracts", tmp_path.to_string_lossy());
  57. let target_path = format!("{repo_path}/test-contract");
  58. create_dir_all(repo_path.clone()).unwrap();
  59. let status = std::process::Command::new("git")
  60. .arg("clone")
  61. .arg("git@github.com:Relayz-io/near-smartcontracts.git")
  62. .arg(&repo_path)
  63. .spawn()
  64. .unwrap()
  65. .wait()
  66. .unwrap();
  67. if !status.success() {
  68. panic!("Failed to clone a near-smartcontracts");
  69. }
  70. workspaces::compile_project(target_path.as_str())
  71. .await
  72. .unwrap()
  73. }
  74. fn random_bits() -> [u8; ED25519_SECRET_KEY_LENGTH] {
  75. let mut chacha = ChaChaRng::from_entropy();
  76. let mut secret_bytes = [0_u8; ED25519_SECRET_KEY_LENGTH];
  77. chacha.fill_bytes(&mut secret_bytes);
  78. secret_bytes
  79. }
  80. // tests themselves
  81. #[tokio::test]
  82. async fn contract_creation() {
  83. let worker = workspaces::sandbox().await.unwrap();
  84. let client = near_client(&worker);
  85. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  86. let signer = create_signer(&worker, &client, &signer_account_id).await;
  87. let wasm = download_contract().await;
  88. client
  89. .deploy_contract(&signer, &signer_account_id, wasm)
  90. .commit(Finality::None)
  91. .await
  92. .unwrap();
  93. }
  94. #[tokio::test]
  95. async fn contract_function_call() {
  96. let worker = workspaces::sandbox().await.unwrap();
  97. let client = near_client(&worker);
  98. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  99. let signer = create_signer(&worker, &client, &signer_account_id).await;
  100. let wasm = download_contract().await;
  101. client
  102. .deploy_contract(&signer, &signer_account_id, wasm)
  103. .commit(Finality::None)
  104. .await
  105. .unwrap();
  106. client
  107. .function_call(&signer, &signer_account_id, "new_default_meta")
  108. .args(json!({
  109. "owner_id": &signer_account_id,
  110. "total_supply": "100",
  111. }))
  112. .gas(near_units::parse_gas!("300 T") as u64)
  113. .commit(Finality::None)
  114. .await
  115. .unwrap();
  116. }
  117. #[tokio::test]
  118. async fn contract_function_call_failed() {
  119. let worker = workspaces::sandbox().await.unwrap();
  120. let client = near_client(&worker);
  121. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  122. let signer = create_signer(&worker, &client, &signer_account_id).await;
  123. let wasm = download_contract().await;
  124. client
  125. .deploy_contract(&signer, &signer_account_id, wasm)
  126. .commit(Finality::None)
  127. .await
  128. .unwrap();
  129. assert!(client
  130. .function_call(&signer, &signer_account_id, "new_default_meta")
  131. .args(json!({
  132. "owner_id": &signer_account_id,
  133. "total_suppl": "100",
  134. }))
  135. .gas(near_units::parse_gas!("300 T") as u64)
  136. .commit(Finality::None)
  137. .await
  138. .is_err());
  139. client
  140. .function_call(&signer, &signer_account_id, "new_default_meta")
  141. .args(json!({
  142. "owner_id": &signer_account_id,
  143. "total_supply": "100",
  144. }))
  145. .gas(near_units::parse_gas!("300 T") as u64)
  146. .commit(Finality::None)
  147. .await
  148. .unwrap();
  149. }
  150. #[tokio::test]
  151. async fn multiple_tests() {
  152. let worker = workspaces::sandbox().await.unwrap();
  153. let client = near_client(&worker);
  154. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  155. let signer = create_signer(&worker, &client, &signer_account_id).await;
  156. let wasm = clone_and_compile_wasm().await;
  157. init_contract(&client, &signer_account_id, &signer, wasm).await;
  158. fc_no_params(&client, &signer_account_id, &signer).await;
  159. fc_with_one_param_and_result(&client, &signer_account_id, &signer).await;
  160. fc_with_param_and_result(&client, &signer_account_id, &signer).await;
  161. view_no_params(&client, &signer_account_id).await;
  162. view_with_params(&client, &signer_account_id).await;
  163. }
  164. async fn init_contract(
  165. client: &NearClient,
  166. contract_id: &AccountId,
  167. signer: &Signer,
  168. wasm: Vec<u8>,
  169. ) {
  170. client
  171. .deploy_contract(signer, contract_id, wasm)
  172. .commit(Finality::None)
  173. .await
  174. .unwrap();
  175. }
  176. async fn view_no_params(client: &NearClient, contract_id: &AccountId) {
  177. client
  178. .view::<u64>(contract_id, Finality::None, "show_id", None)
  179. .await
  180. .unwrap();
  181. }
  182. async fn view_with_params(client: &NearClient, contract_id: &AccountId) {
  183. client
  184. .view::<String>(
  185. contract_id,
  186. Finality::None,
  187. "show_type",
  188. Some(json!({"is_message": true})),
  189. )
  190. .await
  191. .unwrap();
  192. }
  193. // fc = function call
  194. async fn fc_no_params(client: &NearClient, contract_id: &AccountId, signer: &Signer) {
  195. client
  196. .function_call(signer, contract_id, "increment")
  197. .gas(near_units::parse_gas!("300 T") as u64)
  198. .commit(Finality::None)
  199. .await
  200. .unwrap();
  201. }
  202. async fn fc_with_one_param_and_result(
  203. client: &NearClient,
  204. contract_id: &AccountId,
  205. signer: &Signer,
  206. ) {
  207. let expected_result = "change message";
  208. let message = client
  209. .function_call(signer, contract_id, "change_message")
  210. .args(json!({ "message": expected_result }))
  211. .gas(near_units::parse_gas!("300 T") as u64)
  212. .commit(Finality::Final)
  213. .await
  214. .unwrap()
  215. .output::<String>()
  216. .unwrap();
  217. assert_eq!(message, expected_result);
  218. }
  219. async fn fc_with_param_and_result(client: &NearClient, contract_id: &AccountId, signer: &Signer) {
  220. let expected_id = 666u64;
  221. let id = client
  222. .function_call(signer, contract_id, "change_id")
  223. .args(json!({ "id": expected_id }))
  224. .gas(near_units::parse_gas!("300 T") as u64)
  225. .commit(Finality::Final)
  226. .await
  227. .unwrap()
  228. .output::<u64>()
  229. .unwrap();
  230. assert_eq!(id, expected_id);
  231. }
  232. #[tokio::test]
  233. async fn async_transaction() {
  234. let worker = workspaces::sandbox().await.unwrap();
  235. let client = near_client(&worker);
  236. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  237. let signer = create_signer(&worker, &client, &signer_account_id).await;
  238. let wasm = clone_and_compile_wasm().await;
  239. client
  240. .deploy_contract(&signer, &signer_account_id, wasm)
  241. .commit(Finality::None)
  242. .await
  243. .unwrap();
  244. let expected_result = "change message";
  245. let transaction_id = client
  246. .function_call(&signer, &signer_account_id, "change_message")
  247. .args(json!({ "message": expected_result }))
  248. .gas(near_units::parse_gas!("300 T") as u64)
  249. .commit_async(Finality::Final)
  250. .await
  251. .unwrap();
  252. let (tx, rx) = tokio::sync::oneshot::channel::<()>();
  253. tokio::spawn(async move {
  254. tokio::time::timeout(std::time::Duration::from_secs(3), rx)
  255. .await
  256. .expect("Wait async transaction timeout")
  257. });
  258. loop {
  259. let res = client.view_transaction(&transaction_id, &signer).await;
  260. if let Err(near_rpc::Error::ViewTransaction(_)) = &res {
  261. // try one more time
  262. continue;
  263. }
  264. // cancel timeout
  265. tx.send(()).unwrap();
  266. let msg = res.unwrap().output::<String>().unwrap();
  267. assert_eq!(msg, expected_result);
  268. break;
  269. }
  270. }
  271. #[tokio::test]
  272. async fn view_access_key_success() {
  273. let worker = workspaces::sandbox().await.unwrap();
  274. let client = near_client(&worker);
  275. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  276. let signer = create_signer(&worker, &client, &signer_account_id).await;
  277. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  278. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  279. let pk = Ed25519PublicKey::from(&secret_key);
  280. let _ = client
  281. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  282. .commit(Finality::None)
  283. .await
  284. .unwrap()
  285. .output::<serde_json::Value>();
  286. let access_key = client
  287. .view_access_key(&new_acc, &pk, Finality::None)
  288. .await
  289. .unwrap();
  290. assert!(matches!(
  291. access_key,
  292. ViewAccessKey {
  293. result: ViewAccessKeyResult::Ok { .. },
  294. ..
  295. }
  296. ));
  297. }
  298. #[tokio::test]
  299. async fn view_access_key_failure() {
  300. let worker = workspaces::sandbox().await.unwrap();
  301. let client = near_client(&worker);
  302. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  303. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  304. let pk = Ed25519PublicKey::from(&secret_key);
  305. let access_key = client
  306. .view_access_key(&new_acc, &pk, Finality::None)
  307. .await
  308. .unwrap();
  309. assert!(matches!(
  310. access_key,
  311. ViewAccessKey {
  312. result: ViewAccessKeyResult::Err { .. },
  313. ..
  314. }
  315. ));
  316. }
  317. #[tokio::test]
  318. async fn create_account() {
  319. let worker = workspaces::sandbox().await.unwrap();
  320. let client = near_client(&worker);
  321. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  322. let signer = create_signer(&worker, &client, &signer_account_id).await;
  323. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  324. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  325. let pk = Ed25519PublicKey::from(&secret_key);
  326. let _ = client
  327. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  328. .commit(Finality::Final)
  329. .await
  330. .unwrap()
  331. .output::<serde_json::Value>();
  332. let access_key = client
  333. .view_access_key(&new_acc, &pk, Finality::None)
  334. .await
  335. .unwrap();
  336. assert!(matches!(
  337. access_key,
  338. ViewAccessKey {
  339. result: ViewAccessKeyResult::Ok { .. },
  340. ..
  341. }
  342. ));
  343. }
  344. #[tokio::test]
  345. async fn delete_account() {
  346. let worker = workspaces::sandbox().await.unwrap();
  347. let client = near_client(&worker);
  348. let signer_account_id = AccountId::from_str("alice.test.near").unwrap();
  349. let signer = create_signer(&worker, &client, &signer_account_id).await;
  350. let new_acc = AccountId::from_str("one.alice.test.near").unwrap();
  351. let secret_key = Ed25519SecretKey::try_from_bytes(&random_bits()).unwrap();
  352. let pk = Ed25519PublicKey::from(&secret_key);
  353. client
  354. .create_account(&signer, &new_acc, pk, near_units::parse_near!("3 N"))
  355. .commit(Finality::Final)
  356. .await
  357. .unwrap();
  358. let access_key = client
  359. .view_access_key(&new_acc, &pk, Finality::None)
  360. .await
  361. .unwrap();
  362. let nonce = if let ViewAccessKey {
  363. result: ViewAccessKeyResult::Ok(AccessKeyView { nonce, .. }),
  364. ..
  365. } = access_key
  366. {
  367. nonce
  368. } else {
  369. panic!("Can't view access key for just created account")
  370. };
  371. let acc_signer = Signer::from_secret(secret_key, new_acc.clone(), nonce);
  372. client
  373. .delete_account(&acc_signer, &new_acc, &signer_account_id)
  374. .commit(Finality::Final)
  375. .await
  376. .unwrap();
  377. let access_key = client
  378. .view_access_key(&new_acc, &pk, Finality::None)
  379. .await
  380. .unwrap();
  381. assert!(matches!(
  382. access_key,
  383. ViewAccessKey {
  384. result: ViewAccessKeyResult::Err { .. },
  385. ..
  386. }
  387. ));
  388. }
  389. fn temp_dir() -> tempfile::TempDir {
  390. tempfile::Builder::new()
  391. .prefix("near-client-test-")
  392. .tempdir()
  393. .unwrap()
  394. }