create-near-app 8.5.0 → 9.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/app.js +2 -1
  2. package/dist/make.js +7 -27
  3. package/dist/messages.js +1 -9
  4. package/dist/types.js +3 -2
  5. package/dist/user-input.js +32 -13
  6. package/dist/utils/index.js +1 -3
  7. package/package.json +2 -1
  8. package/templates/contracts/auction/rs/Cargo.toml +57 -0
  9. package/templates/contracts/auction/rs/README.md +39 -0
  10. package/templates/contracts/auction/rs/rust-toolchain.toml +4 -0
  11. package/templates/contracts/auction/rs/src/lib.rs +118 -0
  12. package/templates/contracts/auction/rs/tests/test_basics.rs +182 -0
  13. package/templates/contracts/auction/ts/README.md +47 -0
  14. package/templates/contracts/auction/ts/package.json +22 -0
  15. package/templates/contracts/auction/ts/sandbox-test/main.ava.js +88 -0
  16. package/templates/contracts/auction/ts/src/contract.ts +72 -0
  17. package/templates/contracts/auction/ts/tsconfig.json +14 -0
  18. package/templates/contracts/{rs → auction-adv/rs}/Cargo.toml +9 -7
  19. package/templates/contracts/auction-adv/rs/README.md +35 -0
  20. package/templates/contracts/{rs → auction-adv/rs}/rust-toolchain.toml +1 -1
  21. package/templates/contracts/auction-adv/rs/src/ext.rs +17 -0
  22. package/templates/contracts/auction-adv/rs/src/lib.rs +159 -0
  23. package/templates/contracts/auction-adv/rs/tests/fungible_token.wasm +0 -0
  24. package/templates/contracts/auction-adv/rs/tests/non_fungible_token.wasm +0 -0
  25. package/templates/contracts/auction-adv/rs/tests/test_basics.rs +430 -0
  26. package/templates/contracts/auction-adv/ts/README.md +45 -0
  27. package/templates/contracts/auction-adv/ts/package.json +22 -0
  28. package/templates/contracts/auction-adv/ts/sandbox-test/fungible_token.wasm +0 -0
  29. package/templates/contracts/auction-adv/ts/sandbox-test/main.ava.js +165 -0
  30. package/templates/contracts/auction-adv/ts/sandbox-test/non_fungible_token.wasm +0 -0
  31. package/templates/contracts/auction-adv/ts/src/contract.ts +87 -0
  32. package/templates/frontend/next-app/package.json +2 -28
  33. package/templates/frontend/next-app/src/app/hello-near/page.tsx +2 -4
  34. package/templates/frontend/next-app/src/app/layout.tsx +3 -58
  35. package/templates/frontend/next-app/src/components/navigation.tsx +14 -14
  36. package/templates/frontend/next-page/package.json +3 -24
  37. package/templates/frontend/next-page/src/components/cards.tsx +0 -1
  38. package/templates/frontend/next-page/src/components/navigation.tsx +14 -14
  39. package/templates/frontend/next-page/src/pages/_app.tsx +3 -56
  40. package/templates/frontend/next-page/src/pages/hello-near/index.tsx +2 -2
  41. package/templates/frontend/vite-react/package.json +6 -28
  42. package/templates/frontend/vite-react/src/App.tsx +3 -62
  43. package/templates/frontend/vite-react/src/components/navigation.tsx +13 -19
  44. package/templates/frontend/vite-react/src/global.d.ts +13 -0
  45. package/templates/frontend/vite-react/src/pages/hello_near.tsx +3 -3
  46. package/templates/contracts/py/.python-version +0 -1
  47. package/templates/contracts/py/README.md +0 -74
  48. package/templates/contracts/py/contract.py +0 -31
  49. package/templates/contracts/py/pyproject.toml +0 -10
  50. package/templates/contracts/py/tests/test_mod.py +0 -53
  51. package/templates/contracts/py/uv.lock +0 -878
  52. package/templates/contracts/rs/.github/workflows/deploy-production.yml +0 -25
  53. package/templates/contracts/rs/.github/workflows/deploy-staging.yml +0 -52
  54. package/templates/contracts/rs/.github/workflows/test.yml +0 -34
  55. package/templates/contracts/rs/.github/workflows/undeploy-staging.yml +0 -23
  56. package/templates/contracts/rs/README.md +0 -43
  57. package/templates/contracts/rs/src/lib.rs +0 -55
  58. package/templates/contracts/rs/tests/test_basics.rs +0 -30
  59. package/templates/contracts/ts/README.md +0 -83
  60. package/templates/contracts/ts/package.json +0 -23
  61. package/templates/contracts/ts/sandbox-test/main.ava.js +0 -45
  62. package/templates/contracts/ts/src/contract.ts +0 -23
  63. package/templates/contracts/ts/yarn.lock +0 -3290
  64. package/templates/frontend/next-app/src/wallets/web3modal.ts +0 -27
  65. package/templates/frontend/next-page/src/wallets/web3modal.ts +0 -27
  66. package/templates/frontend/vite-react/src/wallets/web3modal.ts +0 -27
  67. /package/templates/contracts/{ts → auction-adv/ts}/tsconfig.json +0 -0
@@ -0,0 +1,430 @@
1
+ use near_api::{AccountId, NearGas, NearToken};
2
+ use near_sdk::json_types::U128;
3
+ use near_sdk::serde_json;
4
+
5
+ const FT_WASM_FILEPATH: &str = "./tests/fungible_token.wasm";
6
+ const NFT_WASM_FILEPATH: &str = "./tests/non_fungible_token.wasm";
7
+
8
+ #[derive(near_sdk::serde::Deserialize)]
9
+ #[serde(crate = "near_sdk::serde")]
10
+ pub struct Bid {
11
+ pub bidder: AccountId,
12
+ pub bid: U128,
13
+ }
14
+
15
+ #[tokio::test]
16
+ async fn test_contract_is_operational() -> testresult::TestResult<()> {
17
+ let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?;
18
+ let contract_wasm = std::fs::read(contract_wasm_path)?;
19
+
20
+ let sandbox = near_sandbox::Sandbox::start_sandbox().await?;
21
+ let sandbox_network =
22
+ near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?);
23
+
24
+ let signer = near_api::Signer::from_secret_key(
25
+ near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY
26
+ .parse()
27
+ .unwrap(),
28
+ )?;
29
+
30
+ let root: AccountId = "sandbox".parse().unwrap();
31
+
32
+ // Create accounts
33
+ let alice = create_subaccount(&sandbox, "alice.sandbox").await?;
34
+ let bob = create_subaccount(&sandbox, "bob.sandbox").await?;
35
+ let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?;
36
+ let contract_account = create_subaccount(&sandbox, "contract.sandbox").await?;
37
+ let contract = contract_account.as_contract();
38
+
39
+ // Deploy and initialize FT contract
40
+ let ft_contract_account = create_subaccount(&sandbox, "ft.sandbox").await?;
41
+ let ft_contract = ft_contract_account.as_contract();
42
+ let ft_wasm = std::fs::read(FT_WASM_FILEPATH)?;
43
+
44
+ near_api::Contract::deploy(ft_contract.account_id().clone())
45
+ .use_code(ft_wasm)
46
+ .with_init_call(
47
+ "new_default_meta",
48
+ serde_json::json!({"owner_id": root, "total_supply": U128(1_000_000)}),
49
+ )?
50
+ .with_signer(signer.clone())
51
+ .send_to(&sandbox_network)
52
+ .await?
53
+ .assert_success();
54
+
55
+ // Deploy and initialize NFT contract
56
+ let nft_contract_account = create_subaccount(&sandbox, "nft.sandbox").await?;
57
+ let nft_contract = nft_contract_account.as_contract();
58
+ let nft_wasm = std::fs::read(NFT_WASM_FILEPATH)?;
59
+
60
+ near_api::Contract::deploy(nft_contract.account_id().clone())
61
+ .use_code(nft_wasm)
62
+ .with_init_call("new_default_meta", serde_json::json!({"owner_id": root}))?
63
+ .with_signer(signer.clone())
64
+ .send_to(&sandbox_network)
65
+ .await?
66
+ .assert_success();
67
+
68
+ // Mint NFT
69
+ nft_contract
70
+ .call_function(
71
+ "nft_mint",
72
+ serde_json::json!({
73
+ "token_id": "1",
74
+ "receiver_id": contract.account_id(),
75
+ "token_metadata": {
76
+ "title": "LEEROYYYMMMJENKINSSS",
77
+ "description": "Alright time's up, let's do this.",
78
+ "media": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse3.mm.bing.net%2Fth%3Fid%3DOIP.Fhp4lHufCdTzTeGCAblOdgHaF7%26pid%3DApi&f=1"
79
+ },
80
+ }),
81
+ )
82
+ .transaction()
83
+ .deposit(NearToken::from_millinear(80))
84
+ .with_signer(root.clone(), signer.clone())
85
+ .send_to(&sandbox_network)
86
+ .await?
87
+ .assert_success();
88
+
89
+ // Register accounts for FT
90
+ for account_id in [
91
+ alice.account_id().clone(),
92
+ bob.account_id().clone(),
93
+ contract.account_id().clone(),
94
+ auctioneer.account_id().clone(),
95
+ ]
96
+ .iter()
97
+ {
98
+ ft_contract
99
+ .call_function("storage_deposit", serde_json::json!({ "account_id": account_id }))
100
+ .transaction()
101
+ .deposit(NearToken::from_yoctonear(8000000000000000000000))
102
+ .with_signer(account_id.clone(), signer.clone())
103
+ .send_to(&sandbox_network)
104
+ .await?
105
+ .assert_success();
106
+ }
107
+
108
+ // Transfer FTs to Alice and Bob
109
+ let transfer_amount = U128(150_000);
110
+
111
+ ft_transfer(
112
+ &ft_contract,
113
+ &root,
114
+ alice.account_id(),
115
+ transfer_amount,
116
+ &signer,
117
+ &sandbox_network,
118
+ )
119
+ .await?;
120
+
121
+ ft_transfer(
122
+ &ft_contract,
123
+ &root,
124
+ bob.account_id(),
125
+ transfer_amount,
126
+ &signer,
127
+ &sandbox_network,
128
+ )
129
+ .await?;
130
+
131
+ // Deploy and initialize auction contract
132
+ let now = std::time::SystemTime::now()
133
+ .duration_since(std::time::SystemTime::UNIX_EPOCH)?
134
+ .as_secs();
135
+ let a_minute_from_now = (now + 60) * 1000000000;
136
+ let starting_price = U128(10_000);
137
+
138
+ near_api::Contract::deploy(contract.account_id().clone())
139
+ .use_code(contract_wasm)
140
+ .with_init_call(
141
+ "init",
142
+ serde_json::json!({
143
+ "end_time": a_minute_from_now.to_string(),
144
+ "auctioneer": auctioneer.account_id(),
145
+ "ft_contract": ft_contract.account_id(),
146
+ "nft_contract": nft_contract.account_id(),
147
+ "token_id": "1",
148
+ "starting_price": starting_price
149
+ }),
150
+ )?
151
+ .with_signer(signer.clone())
152
+ .send_to(&sandbox_network)
153
+ .await?
154
+ .assert_success();
155
+
156
+ // Alice makes bid less than starting price
157
+ ft_transfer_call(
158
+ &ft_contract,
159
+ &alice,
160
+ contract.account_id(),
161
+ U128(5_000),
162
+ &signer,
163
+ &sandbox_network,
164
+ )
165
+ .await?;
166
+
167
+ let highest_bid: Bid = contract
168
+ .call_function("get_highest_bid", ())
169
+ .read_only()
170
+ .fetch_from(&sandbox_network)
171
+ .await?
172
+ .data;
173
+ assert_eq!(highest_bid.bid, U128(10_000));
174
+ assert_eq!(&highest_bid.bidder, contract.account_id());
175
+
176
+ let contract_balance: U128 =
177
+ ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?;
178
+ assert_eq!(contract_balance, U128(0));
179
+
180
+ let alice_balance: U128 =
181
+ ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?;
182
+ assert_eq!(alice_balance, U128(150_000));
183
+
184
+ // Alice makes valid bid
185
+ ft_transfer_call(
186
+ &ft_contract,
187
+ &alice,
188
+ contract.account_id(),
189
+ U128(50_000),
190
+ &signer,
191
+ &sandbox_network,
192
+ )
193
+ .await?;
194
+
195
+ let highest_bid: Bid = contract
196
+ .call_function("get_highest_bid", ())
197
+ .read_only()
198
+ .fetch_from(&sandbox_network)
199
+ .await?
200
+ .data;
201
+ assert_eq!(highest_bid.bid, U128(50_000));
202
+ assert_eq!(&highest_bid.bidder, alice.account_id());
203
+
204
+ let contract_balance: U128 =
205
+ ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?;
206
+ assert_eq!(contract_balance, U128(50_000));
207
+
208
+ let alice_balance: U128 =
209
+ ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?;
210
+ assert_eq!(alice_balance, U128(100_000));
211
+
212
+ // Bob makes a higher bid
213
+ ft_transfer_call(
214
+ &ft_contract,
215
+ &bob,
216
+ contract.account_id(),
217
+ U128(60_000),
218
+ &signer,
219
+ &sandbox_network,
220
+ )
221
+ .await?;
222
+
223
+ let highest_bid: Bid = contract
224
+ .call_function("get_highest_bid", ())
225
+ .read_only()
226
+ .fetch_from(&sandbox_network)
227
+ .await?
228
+ .data;
229
+ assert_eq!(highest_bid.bid, U128(60_000));
230
+ assert_eq!(&highest_bid.bidder, bob.account_id());
231
+
232
+ // Checks Alice was returned her bid
233
+ let alice_balance: U128 =
234
+ ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?;
235
+ assert_eq!(alice_balance, U128(150_000));
236
+
237
+ let bob_balance: U128 =
238
+ ft_balance_of(&ft_contract, bob.account_id(), &sandbox_network).await?;
239
+ assert_eq!(bob_balance, U128(90_000));
240
+
241
+ // Alice tries to make a bid with less FTs than the previous
242
+ ft_transfer_call(
243
+ &ft_contract,
244
+ &alice,
245
+ contract.account_id(),
246
+ U128(50_000),
247
+ &signer,
248
+ &sandbox_network,
249
+ )
250
+ .await?;
251
+
252
+ let highest_bid: Bid = contract
253
+ .call_function("get_highest_bid", ())
254
+ .read_only()
255
+ .fetch_from(&sandbox_network)
256
+ .await?
257
+ .data;
258
+ assert_eq!(highest_bid.bid, U128(60_000));
259
+ assert_eq!(&highest_bid.bidder, bob.account_id());
260
+
261
+ let contract_balance: U128 =
262
+ ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?;
263
+ assert_eq!(contract_balance, U128(60_000));
264
+
265
+ let alice_balance: U128 =
266
+ ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?;
267
+ assert_eq!(alice_balance, U128(150_000));
268
+
269
+ // Auctioneer claims auction but did not finish
270
+ contract
271
+ .call_function("claim", ())
272
+ .transaction()
273
+ .gas(NearGas::from_tgas(300))
274
+ .with_signer(auctioneer.account_id().clone(), signer.clone())
275
+ .send_to(&sandbox_network)
276
+ .await?
277
+ .assert_failure();
278
+
279
+ // Fast forward 200 blocks
280
+ let blocks_to_advance = 200;
281
+ sandbox.fast_forward(blocks_to_advance).await?;
282
+
283
+ // Auctioneer claims auction
284
+ contract
285
+ .call_function("claim", ())
286
+ .transaction()
287
+ .gas(NearGas::from_tgas(300))
288
+ .with_signer(auctioneer.account_id().clone(), signer.clone())
289
+ .send_to(&sandbox_network)
290
+ .await?
291
+ .assert_success();
292
+
293
+ let contract_balance: U128 =
294
+ ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?;
295
+ assert_eq!(contract_balance, U128(0));
296
+
297
+ let auctioneer_balance: U128 =
298
+ ft_balance_of(&ft_contract, auctioneer.account_id(), &sandbox_network).await?;
299
+ assert_eq!(auctioneer_balance, U128(60_000));
300
+
301
+ // Check highest bidder received the NFT
302
+ let token_info: serde_json::Value = nft_contract
303
+ .call_function("nft_token", serde_json::json!({"token_id": "1"}))
304
+ .read_only()
305
+ .fetch_from(&sandbox_network)
306
+ .await?
307
+ .data;
308
+ let owner_id: String = token_info["owner_id"].as_str().unwrap().to_string();
309
+
310
+ assert_eq!(
311
+ owner_id,
312
+ bob.account_id().to_string(),
313
+ "token owner is not the highest bidder"
314
+ );
315
+
316
+ // Auctioneer claims auction back but fails
317
+ contract
318
+ .call_function("claim", ())
319
+ .transaction()
320
+ .gas(NearGas::from_tgas(300))
321
+ .with_signer(auctioneer.account_id().clone(), signer.clone())
322
+ .send_to(&sandbox_network)
323
+ .await?
324
+ .assert_failure();
325
+
326
+ // Alice tries to make a bid when the auction is over
327
+ ft_transfer_call(
328
+ &ft_contract,
329
+ &alice,
330
+ contract.account_id(),
331
+ U128(70_000),
332
+ &signer,
333
+ &sandbox_network,
334
+ )
335
+ .await?;
336
+
337
+ let highest_bid: Bid = contract
338
+ .call_function("get_highest_bid", ())
339
+ .read_only()
340
+ .fetch_from(&sandbox_network)
341
+ .await?
342
+ .data;
343
+ assert_eq!(highest_bid.bid, U128(60_000));
344
+ assert_eq!(&highest_bid.bidder, bob.account_id());
345
+
346
+ let contract_balance: U128 =
347
+ ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?;
348
+ assert_eq!(contract_balance, U128(0));
349
+
350
+ let alice_balance: U128 =
351
+ ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?;
352
+ assert_eq!(alice_balance, U128(150_000));
353
+
354
+ let bob_balance: U128 =
355
+ ft_balance_of(&ft_contract, bob.account_id(), &sandbox_network).await?;
356
+ assert_eq!(bob_balance, U128(90_000));
357
+
358
+ Ok(())
359
+ }
360
+
361
+ async fn create_subaccount(
362
+ sandbox: &near_sandbox::Sandbox,
363
+ name: &str,
364
+ ) -> testresult::TestResult<near_api::Account> {
365
+ let account_id: AccountId = name.parse().unwrap();
366
+ sandbox
367
+ .create_account(account_id.clone())
368
+ .initial_balance(NearToken::from_near(10))
369
+ .send()
370
+ .await?;
371
+ Ok(near_api::Account(account_id))
372
+ }
373
+
374
+ async fn ft_transfer(
375
+ ft_contract: &near_api::Contract,
376
+ from: &AccountId,
377
+ to: &AccountId,
378
+ amount: U128,
379
+ signer: &std::sync::Arc<near_api::Signer>,
380
+ network: &near_api::NetworkConfig,
381
+ ) -> testresult::TestResult<()> {
382
+ ft_contract
383
+ .call_function(
384
+ "ft_transfer",
385
+ serde_json::json!({"receiver_id": to, "amount": amount}),
386
+ )
387
+ .transaction()
388
+ .deposit(NearToken::from_yoctonear(1))
389
+ .with_signer(from.clone(), signer.clone())
390
+ .send_to(network)
391
+ .await?
392
+ .assert_success();
393
+ Ok(())
394
+ }
395
+
396
+ async fn ft_balance_of(
397
+ ft_contract: &near_api::Contract,
398
+ account_id: &AccountId,
399
+ network: &near_api::NetworkConfig,
400
+ ) -> testresult::TestResult<U128> {
401
+ let result: U128 = ft_contract
402
+ .call_function("ft_balance_of", serde_json::json!({"account_id": account_id}))
403
+ .read_only()
404
+ .fetch_from(network)
405
+ .await?
406
+ .data;
407
+ Ok(result)
408
+ }
409
+
410
+ async fn ft_transfer_call(
411
+ ft_contract: &near_api::Contract,
412
+ account: &near_api::Account,
413
+ receiver_id: &AccountId,
414
+ amount: U128,
415
+ signer: &std::sync::Arc<near_api::Signer>,
416
+ network: &near_api::NetworkConfig,
417
+ ) -> testresult::TestResult<()> {
418
+ let _ = ft_contract
419
+ .call_function(
420
+ "ft_transfer_call",
421
+ serde_json::json!({"receiver_id": receiver_id, "amount": amount, "msg": "0"}),
422
+ )
423
+ .transaction()
424
+ .deposit(NearToken::from_yoctonear(1))
425
+ .gas(NearGas::from_tgas(300))
426
+ .with_signer(account.account_id().clone(), signer.clone())
427
+ .send_to(network)
428
+ .await?;
429
+ Ok(())
430
+ }
@@ -0,0 +1,45 @@
1
+ # Auction Contract in FTs
2
+
3
+ This directory contains a JavaScript contract that is used as part of the [Bidding with FTs](https://docs.near.org/tutorials/auction/bidding-with-fts) section of the auction tutorial.
4
+
5
+ In this part the contract is adapted so users can bid in fungible tokens (FTs) instead of NEAR tokens. It is a great way to learn how to work with FTs in NEAR.
6
+
7
+ ---
8
+
9
+ ## How to Build Locally?
10
+
11
+ Install the [NEAR CLI](https://docs.near.org/tools/near-cli#installation) and run:
12
+
13
+ Install the dependencies:
14
+
15
+ ```bash
16
+ npm install
17
+ ```
18
+
19
+ Build the contract:
20
+
21
+ ```bash
22
+ npm run build
23
+ ```
24
+
25
+ ## How to Test Locally?
26
+
27
+ ```bash
28
+ npm run test
29
+ ```
30
+
31
+ ## How to Deploy?
32
+
33
+ Install the [NEAR CLI](https://docs.near.org/tools/near-cli#installation) and run:
34
+
35
+ ```bash
36
+ # Create a new account
37
+ near create <contractId> --useFaucet
38
+
39
+ # Deploy the contract
40
+ near deploy <contractId> ./build/auction-contract.wasm
41
+
42
+ # Initialize the contract
43
+ TWO_MINUTES_FROM_NOW=$(date -v+2M +%s000000000)
44
+ near call <contractId> init '{"end_time": "'$TWO_MINUTES_FROM_NOW'", "auctioneer": "<auctioneerAccountId>", "nft_contract": "<nftContractId>", "token_id": "<tokenId>"}' --accountId <contractId>
45
+ ```
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "auction-contract",
3
+ "version": "1.0.0",
4
+ "license": "(MIT AND Apache-2.0)",
5
+ "type": "module",
6
+ "scripts": {
7
+ "build": "near-sdk-js build src/contract.ts build/auction-contract.wasm",
8
+ "test": "$npm_execpath run build && ava -- ./build/auction-contract.wasm"
9
+ },
10
+ "dependencies": {
11
+ "near-sdk-js": "2.0.0"
12
+ },
13
+ "devDependencies": {
14
+ "ava": "^6.1.3",
15
+ "near-workspaces": "^3.5.0",
16
+ "typescript": "^5.4.5"
17
+ },
18
+ "ava": {
19
+ "timeout": "50000",
20
+ "files": ["sandbox-test/*.ava.js"]
21
+ }
22
+ }
@@ -0,0 +1,165 @@
1
+
2
+
3
+ import anyTest from 'ava';
4
+ import { NEAR, Worker } from 'near-workspaces';
5
+ import { setDefaultResultOrder } from 'dns'; setDefaultResultOrder('ipv4first'); // temp fix for node >v17
6
+
7
+
8
+ /**
9
+ * @typedef {import('near-workspaces').NearAccount} NearAccount
10
+ * @type {import('ava').TestFn<{worker: Worker, accounts: Record<string, NearAccount>}>}
11
+ */
12
+ const test = anyTest;
13
+ const FT_WASM_FILEPATH = "./sandbox-test/fungible_token.wasm";
14
+ const NFT_WASM_FILEPATH = "./sandbox-test/non_fungible_token.wasm";
15
+ test.beforeEach(async (t) => {
16
+ // Init the worker and start a Sandbox server
17
+ const worker = t.context.worker = await Worker.init();
18
+
19
+ // Create accounts
20
+ const root = worker.rootAccount;
21
+
22
+ const alice = await root.createSubAccount("alice",{initialBalance: NEAR.parse("10 N").toString()});
23
+ const bob = await root.createSubAccount("bob",{initialBalance: NEAR.parse("10 N").toString()});
24
+ const contract = await root.createSubAccount("contract",{initialBalance: NEAR.parse("10 N").toString()});
25
+ const auctioneer = await root.createSubAccount("auctioneer",{initialBalance: NEAR.parse("10 N").toString()});
26
+
27
+ // Deploy and initialize FT contract
28
+ const ft_contract = await root.devDeploy(FT_WASM_FILEPATH);
29
+ await ft_contract.call(ft_contract,"new_default_meta",{"owner_id":ft_contract.accountId,"total_supply":BigInt(1_000_000).toString()});
30
+
31
+ // Deploy and initialize NFT contract
32
+ const nft_contract = await root.devDeploy(NFT_WASM_FILEPATH);
33
+ await nft_contract.call(nft_contract, "new_default_meta", { "owner_id": nft_contract.accountId });
34
+
35
+ // Mint NFT
36
+ const token_id = "1";
37
+ let request_payload = {
38
+ "token_id": token_id,
39
+ "receiver_id": contract.accountId,
40
+ "metadata": {
41
+ "title": "LEEROYYYMMMJENKINSSS",
42
+ "description": "Alright time's up, let's do this.",
43
+ "media": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse3.mm.bing.net%2Fth%3Fid%3DOIP.Fhp4lHufCdTzTeGCAblOdgHaF7%26pid%3DApi&f=1"
44
+ },
45
+ };
46
+
47
+ await nft_contract.call(nft_contract,"nft_mint",request_payload,{ attachedDeposit: NEAR.from("8000000000000000000000").toString(),gas: "300000000000000" });
48
+
49
+ // Register accounts in FT contract
50
+ const contracts = [alice,bob,contract,auctioneer];
51
+ for (const contract_to_register of contracts) {
52
+ await contract_to_register.call(ft_contract, "storage_deposit",{ "account_id": contract_to_register.accountId },{ attachedDeposit: NEAR.from("8000000000000000000000").toString(),gas: "300000000000000" })
53
+ }
54
+
55
+ // Give accounts FTs
56
+ await ft_contract.call(ft_contract,"ft_transfer",{"receiver_id":alice.accountId,"amount":BigInt(150_000).toString()},{ attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
57
+ await ft_contract.call(ft_contract,"ft_transfer",{"receiver_id":bob.accountId,"amount":BigInt(150_000).toString()},{ attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
58
+
59
+ // Deploy contract (input from package.json)
60
+ await contract.deploy(process.argv[2]);
61
+
62
+ // Initialize contract, finishes in 1 minute
63
+ await contract.call(contract, "init", {
64
+ end_time: String((Date.now() + 60000) * 10 ** 6),
65
+ auctioneer: auctioneer.accountId,
66
+ ft_contract: ft_contract.accountId,
67
+ nft_contract: nft_contract.accountId,
68
+ token_id: token_id,
69
+ starting_price: BigInt(10_000).toString()
70
+ });
71
+
72
+ // Save state for test runs, it is unique for each test
73
+ t.context.worker = worker;
74
+ t.context.accounts = { alice, bob, contract, auctioneer,ft_contract,nft_contract};
75
+ });
76
+
77
+
78
+ test.afterEach.always(async (t) => {
79
+ // Stop Sandbox server
80
+ await t.context.worker.tearDown().catch((error) => {
81
+ console.log('Failed to stop the Sandbox:', error);
82
+ });
83
+ });
84
+
85
+
86
+ test("Test full contract", async (t) => {
87
+ const { alice, bob, auctioneer, contract, nft_contract, ft_contract } = t.context.accounts;
88
+
89
+ // Alice makes bid less than starting price
90
+ await alice.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(5_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
91
+ let highest_bid = await contract.view("get_highest_bid", {});
92
+ t.is(highest_bid.bidder, contract.accountId);
93
+ t.is(highest_bid.bid, BigInt(10_000).toString());
94
+
95
+ let contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId})
96
+ t.is(contract_balance, BigInt(0).toString());
97
+ let alice_balance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId})
98
+ t.is(alice_balance, BigInt(150_000).toString());
99
+
100
+ // Alice makes valid bid
101
+ await alice.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(50_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
102
+ highest_bid = await contract.view("get_highest_bid", {});
103
+ t.is(highest_bid.bidder, alice.accountId);
104
+ t.is(highest_bid.bid, BigInt(50_000).toString());
105
+
106
+ contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId})
107
+ t.is(contract_balance, BigInt(50_000).toString());
108
+ alice_balance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId})
109
+ t.is(alice_balance, BigInt(100_000).toString());
110
+
111
+ // Bob makes a higher bid
112
+ await bob.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(60_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
113
+ highest_bid = await contract.view("get_highest_bid", {});
114
+ t.is(highest_bid.bidder, bob.accountId);
115
+ t.is(highest_bid.bid, BigInt(60_000).toString());
116
+
117
+ // Check Alice recieved her bid back
118
+ const aliceNewBalance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId});
119
+ t.is(BigInt(150_000).toString(),aliceNewBalance);
120
+
121
+ // Alice tries to make a bid with less FTs than the previous
122
+ await alice.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(50_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
123
+ highest_bid = await contract.view("get_highest_bid", {});
124
+ t.is(highest_bid.bidder, bob.accountId);
125
+ t.is(highest_bid.bid, BigInt(60_000).toString());
126
+
127
+ contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId})
128
+ t.is(contract_balance, BigInt(60_000).toString());
129
+ alice_balance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId})
130
+ t.is(alice_balance, BigInt(150_000).toString());
131
+
132
+ // Auctioneer claims auction but did not finish
133
+ await t.throwsAsync(auctioneer.call(contract, "claim",{},{ gas: "300000000000000" }))
134
+
135
+ // Fast forward 200 blocks
136
+ await t.context.worker.provider.fastForward(200)
137
+
138
+ // Auctioneer claims auction
139
+ await auctioneer.call(contract, "claim",{},{ gas: "300000000000000" });
140
+
141
+ contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId})
142
+ t.is(contract_balance, BigInt(0).toString());
143
+ const auctioneer_balance = await ft_contract.view("ft_balance_of",{"account_id": auctioneer.accountId});
144
+ t.is(auctioneer_balance, BigInt(60_000).toString());
145
+
146
+ // Check highest bidder received the NFT
147
+ const response = await nft_contract.call(nft_contract, "nft_token",{"token_id": "1"},{ gas: "300000000000000" });
148
+ t.is(response.owner_id,bob.accountId);
149
+
150
+ // Auctioneer claims auction back but fails
151
+ await t.throwsAsync(auctioneer.call(contract, "claim",{},{ gas: "300000000000000" }))
152
+
153
+ // Alice tries to make a bid when the auction is over
154
+ await alice.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(70_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" });
155
+ highest_bid = await contract.view("get_highest_bid", {});
156
+ t.is(highest_bid.bidder, bob.accountId);
157
+ t.is(highest_bid.bid, BigInt(60_000).toString());
158
+
159
+ contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId})
160
+ t.is(contract_balance, BigInt(0).toString());
161
+ alice_balance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId})
162
+ t.is(alice_balance, BigInt(150_000).toString());
163
+ let bob_balance = await ft_contract.view("ft_balance_of",{"account_id": bob.accountId})
164
+ t.is(bob_balance, BigInt(90_000).toString());
165
+ });