diff --git a/contract-rs/01-basic-auction/Cargo.toml b/contract-rs/01-basic-auction/Cargo.toml index f871a651..a57c4dd8 100644 --- a/contract-rs/01-basic-auction/Cargo.toml +++ b/contract-rs/01-basic-auction/Cargo.toml @@ -9,14 +9,20 @@ crate-type = ["cdylib", "rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -near-sdk = "5.23.0" +near-sdk = "5.24.0" [dev-dependencies] -near-sdk = { version = "5.23.0", features = ["unit-testing"] } -near-workspaces = { version = "0.22.0", features = ["unstable"] } +near-sdk = { version = "5.24.0", features = ["unit-testing"] } +near-sandbox = "0.3" +near-api = "0.8" +cargo-near-build = "0.10" tokio = { version = "1.12.0", features = ["full"] } serde_json = "1" -chrono = "0.4.38" +testresult = "0.4.1" +# This is temporary fix for the build error since those crates with a higher version require a higher version of Rust compiler (1.88.0) +cargo-platform = "=0.3.1" +darling = "=0.20.11" +bon = "=3.8.1" [profile.release] codegen-units = 1 diff --git a/contract-rs/01-basic-auction/tests/test_basics.rs b/contract-rs/01-basic-auction/tests/test_basics.rs index a8c0fecf..e546f4ed 100644 --- a/contract-rs/01-basic-auction/tests/test_basics.rs +++ b/contract-rs/01-basic-auction/tests/test_basics.rs @@ -1,148 +1,189 @@ -use chrono::Utc; -use near_sdk::near; -use near_workspaces::types::{AccountId, Gas, NearToken}; -use serde_json::json; +use near_api::{AccountId, NearGas, NearToken}; +use near_sdk::serde_json::json; -#[near(serializers = [json])] -#[derive(Clone)] +#[derive(near_sdk::serde::Deserialize)] +#[serde(crate = "near_sdk::serde")] pub struct Bid { pub bidder: AccountId, pub bid: NearToken, } -const TEN_NEAR: NearToken = NearToken::from_near(10); - #[tokio::test] -async fn test_contract_is_operational() -> Result<(), Box> { - let sandbox = near_workspaces::sandbox().await?; +async fn test_contract_is_operational() -> testresult::TestResult<()> { + // Build the contract wasm file + let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?; + let contract_wasm = std::fs::read(contract_wasm_path)?; - let root = sandbox.root_account()?; + // Initialize the sandbox + let sandbox = near_sandbox::Sandbox::start_sandbox().await?; + let sandbox_network = + near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?); // Create accounts - let alice = create_subaccount(&root, "alice").await?; - let bob = create_subaccount(&root, "bob").await?; - let auctioneer = create_subaccount(&root, "auctioneer").await?; - let contract_account = create_subaccount(&root, "contract").await?; - - // Deploy and initialize contract - let contract_wasm = near_workspaces::compile_project("./").await?; - let contract = contract_account.deploy(&contract_wasm).await?.unwrap(); - - let now = Utc::now().timestamp(); + let alice = create_subaccount(&sandbox, "alice.sandbox").await?; + let bob = create_subaccount(&sandbox, "bob.sandbox").await?; + let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; + let contract = create_subaccount(&sandbox, "contract.sandbox") + .await? + .as_contract(); + + // Initialize signer for the contract deployment + let signer = near_api::Signer::from_secret_key( + near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY + .parse() + .unwrap(), + )?; + + // Calculate the end time for the auction as a parameter for the init function + let now = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(); let a_minute_from_now = (now + 60) * 1000000000; - let init = contract - .call("init") - .args_json(json!({"end_time": a_minute_from_now.to_string(),"auctioneer":auctioneer.id()})) - .transact() - .await?; - - assert!(init.is_success()); + // Deploy the contract with the init call + near_api::Contract::deploy(contract.account_id().clone()) + .use_code(contract_wasm) + .with_init_call( + "init", + json!({"end_time": a_minute_from_now.to_string(), "auctioneer": auctioneer.account_id()}), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Alice makes first bid - let alice_bid = alice - .call(contract.id(), "bid") + contract + .call_function("bid", ()) + .transaction() .deposit(NearToken::from_near(1)) - .transact() - .await?; - - assert!(alice_bid.is_success()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - let highest_bid_json = contract.view("get_highest_bid").await?; - let highest_bid: Bid = highest_bid_json.json::()?; + // For now, the highest bid is the Alice's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; assert_eq!(highest_bid.bid, NearToken::from_near(1)); - assert_eq!(highest_bid.bidder, *alice.id()); + assert_eq!(&highest_bid.bidder, alice.account_id()); - let alice_balance = alice.view_account().await?.balance; + let alice_balance = alice + .tokens() + .near_balance() + .fetch_from(&sandbox_network) + .await? + .total; - // Bob makes a higher bid - let bob_bid = bob - .call(contract.id(), "bid") + // Now, Bob makes a higher bid + contract + .call_function("bid", ()) + .transaction() .deposit(NearToken::from_near(2)) - .transact() - .await?; - - assert!(bob_bid.is_success()); + .with_signer(bob.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - let highest_bid_json = contract.view("get_highest_bid").await?; - let highest_bid: Bid = highest_bid_json.json::()?; + // Now, the highest bid is the Bob's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; assert_eq!(highest_bid.bid, NearToken::from_near(2)); - assert_eq!(highest_bid.bidder, *bob.id()); + assert_eq!(&highest_bid.bidder, bob.account_id()); - // Check that Alice was returned her bid - let new_alice_balance = alice.view_account().await?.balance; + // Check that Alice was refunded her bid + let new_alice_balance = alice + .tokens() + .near_balance() + .fetch_from(&sandbox_network) + .await? + .total; assert!(new_alice_balance == alice_balance.saturating_add(NearToken::from_near(1))); // Alice tries to make a bid with less NEAR than the previous - let alice_bid = alice - .call(contract.id(), "bid") + contract + .call_function("bid", ()) + .transaction() .deposit(NearToken::from_near(1)) - .transact() - .await?; - - assert!(alice_bid.is_failure()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Auctioneer claims auction but did not finish - let auctioneer_claim = auctioneer - .call(contract_account.id(), "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - - assert!(auctioneer_claim.is_failure()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(30)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Fast forward 200 blocks let blocks_to_advance = 200; sandbox.fast_forward(blocks_to_advance).await?; // Auctioneer claims the auction - let auctioneer_claim = auctioneer - .call(contract_account.id(), "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - - assert!(auctioneer_claim.is_success()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(30)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Checks the auctioneer has the correct balance - let auctioneer_balance = auctioneer.view_account().await?.balance; + let auctioneer_balance = auctioneer + .tokens() + .near_balance() + .fetch_from(&sandbox_network) + .await? + .total; assert!(auctioneer_balance <= NearToken::from_near(12)); assert!(auctioneer_balance > NearToken::from_millinear(11990)); // Auctioneer tries to claim the auction again - let auctioneer_claim = auctioneer - .call(contract_account.id(), "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - - assert!(auctioneer_claim.is_failure()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(30)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Alice tries to make a bid when the auction is over - let alice_bid = alice - .call(contract.id(), "bid") - .deposit(NearToken::from_near(3)) - .transact() - .await?; - - assert!(alice_bid.is_failure()); + contract + .call_function("bid", ()) + .transaction() + .deposit(NearToken::from_near(1)) + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); Ok(()) } async fn create_subaccount( - root: &near_workspaces::Account, + sandbox: &near_sandbox::Sandbox, name: &str, -) -> Result> { - let subaccount = root - .create_subaccount(name) - .initial_balance(TEN_NEAR) - .transact() - .await? - .unwrap(); - - Ok(subaccount) +) -> testresult::TestResult { + let account_id: AccountId = name.parse().unwrap(); + sandbox + .create_account(account_id.clone()) + .initial_balance(NearToken::from_near(10)) + .send() + .await?; + Ok(near_api::Account(account_id)) } diff --git a/contract-rs/02-winner-gets-nft/Cargo.toml b/contract-rs/02-winner-gets-nft/Cargo.toml index d1a9e383..11ba3177 100644 --- a/contract-rs/02-winner-gets-nft/Cargo.toml +++ b/contract-rs/02-winner-gets-nft/Cargo.toml @@ -9,14 +9,20 @@ crate-type = ["cdylib", "rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -near-sdk = "5.23.0" +near-sdk = "5.24.0" [dev-dependencies] -near-sdk = { version = "5.23.0", features = ["unit-testing"] } -near-workspaces = { version = "0.22.0", features = ["unstable"] } +near-sdk = { version = "5.24.0", features = ["unit-testing"] } +near-sandbox = "0.3" +near-api = "0.8" +cargo-near-build = "0.10" +testresult = "0.4.1" tokio = { version = "1.12.0", features = ["full"] } serde_json = "1" -chrono = "0.4.38" +# This is temporary fix for the build error since those crates with a higher version require a higher version of Rust compiler (1.88.0) +cargo-platform = "=0.3.1" +darling = "=0.20.11" +bon = "=3.8.1" [profile.release] codegen-units = 1 diff --git a/contract-rs/02-winner-gets-nft/tests/test_basics.rs b/contract-rs/02-winner-gets-nft/tests/test_basics.rs index 1e90dfdb..6625f044 100644 --- a/contract-rs/02-winner-gets-nft/tests/test_basics.rs +++ b/contract-rs/02-winner-gets-nft/tests/test_basics.rs @@ -1,48 +1,64 @@ -use chrono::Utc; -use near_sdk::near; -use near_workspaces::result::ExecutionFinalResult; -use near_workspaces::types::{AccountId, Gas, NearToken}; -use serde_json::json; - -#[near(serializers = [json])] -#[derive(Clone)] +use near_api::{AccountId, NearGas, NearToken}; +use near_sdk::serde_json::json; + +#[derive(near_sdk::serde::Deserialize)] +#[serde(crate = "near_sdk::serde")] pub struct Bid { pub bidder: AccountId, pub bid: NearToken, } -const TEN_NEAR: NearToken = NearToken::from_near(10); const NFT_WASM_FILEPATH: &str = "./tests/non_fungible_token.wasm"; #[tokio::test] -async fn test_contract_is_operational() -> Result<(), Box> { - let sandbox = near_workspaces::sandbox().await?; - - let root: near_workspaces::Account = sandbox.root_account()?; - - // Create accounts - let alice = create_subaccount(&root, "alice").await?; - let bob = create_subaccount(&root, "bob").await?; - let auctioneer = create_subaccount(&root, "auctioneer").await?; - let contract_account = create_subaccount(&root, "contract").await?; +async fn test_contract_is_operational() -> testresult::TestResult<()> { + // Build the contract wasm file + let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?; + let contract_wasm = std::fs::read(contract_wasm_path)?; - // Deploy and initialize NFT contract + // Read the NFT wasm file let nft_wasm = std::fs::read(NFT_WASM_FILEPATH)?; - let nft_contract = sandbox.dev_deploy(&nft_wasm).await?; - let res = nft_contract - .call("new_default_meta") - .args_json(serde_json::json!({"owner_id": root.id()})) - .transact() - .await?; + // Initialize the sandbox + let sandbox = near_sandbox::Sandbox::start_sandbox().await?; + let sandbox_network = + near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?); - assert!(res.is_success()); + // Create accounts + let alice = create_subaccount(&sandbox, "alice.sandbox").await?; + let bob = create_subaccount(&sandbox, "bob.sandbox").await?; + let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; + let nft_contract = create_subaccount(&sandbox, "nft-contract.sandbox") + .await? + .as_contract(); + let contract = create_subaccount(&sandbox, "contract.sandbox") + .await? + .as_contract(); + + // Initialize signer for the contract deployment + let signer = near_api::Signer::from_secret_key( + near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY + .parse() + .unwrap(), + )?; + + // Deploy the NFT contract + near_api::Contract::deploy(nft_contract.account_id().clone()) + .use_code(nft_wasm) + .with_init_call( + "new_default_meta", + json!({"owner_id": nft_contract.account_id()}), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Mint NFT const TOKEN_ID: &str = "1"; let request_payload = json!({ "token_id": TOKEN_ID, - "receiver_id": contract_account.id(), + "receiver_id": contract.account_id(), "token_metadata": { "title": "LEEROYYYMMMJENKINSSS", "description": "Alright time's up, let's do this.", @@ -50,151 +66,178 @@ async fn test_contract_is_operational() -> Result<(), Box }, }); - let res = contract_account - .call(nft_contract.id(), "nft_mint") - .args_json(request_payload) + nft_contract + .call_function("nft_mint", request_payload) + .transaction() .deposit(NearToken::from_millinear(80)) - .transact() - .await?; - - assert!(res.is_success()); - - // Deploy and initialize auction contract - let contract_wasm = near_workspaces::compile_project("./").await?; - let contract = contract_account.deploy(&contract_wasm).await?.unwrap(); + .with_signer(nft_contract.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - let now = Utc::now().timestamp(); + // Deploy the contract with the init call + let now = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(); let a_minute_from_now = (now + 60) * 1000000000; - - let init: ExecutionFinalResult = contract - .call("init") - .args_json( - json!({"end_time": a_minute_from_now.to_string(),"auctioneer": auctioneer.id(),"nft_contract":nft_contract.id(),"token_id":TOKEN_ID }), - ) - .transact() - .await?; - - assert!(init.is_success()); + near_api::Contract::deploy(contract.account_id().clone()) + .use_code(contract_wasm) + .with_init_call( + "init", + json!({"end_time": a_minute_from_now.to_string(), "auctioneer": auctioneer.account_id(), "nft_contract": nft_contract.account_id(), "token_id": TOKEN_ID}), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Alice makes first bid - let alice_bid = alice - .call(contract.id(), "bid") + contract + .call_function("bid", ()) + .transaction() .deposit(NearToken::from_near(1)) - .transact() - .await?; - - assert!(alice_bid.is_success()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - let highest_bid_json = contract.view("get_highest_bid").await?; - let highest_bid: Bid = highest_bid_json.json::()?; + // For now, the highest bid is the Alice's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; assert_eq!(highest_bid.bid, NearToken::from_near(1)); - assert_eq!(highest_bid.bidder, *alice.id()); + assert_eq!(&highest_bid.bidder, alice.account_id()); - let alice_balance = alice.view_account().await?.balance; + let alice_balance = alice + .tokens() + .near_balance() + .fetch_from(&sandbox_network) + .await? + .total; - // Bob makes a higher bid - let bob_bid = bob - .call(contract.id(), "bid") + // Now, Bob makes a higher bid + contract + .call_function("bid", ()) + .transaction() .deposit(NearToken::from_near(2)) - .transact() - .await?; - - assert!(bob_bid.is_success()); + .with_signer(bob.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - let highest_bid_json = contract.view("get_highest_bid").await?; - let highest_bid: Bid = highest_bid_json.json::()?; + // Now, the highest bid is the Bob's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; assert_eq!(highest_bid.bid, NearToken::from_near(2)); - assert_eq!(highest_bid.bidder, *bob.id()); + assert_eq!(&highest_bid.bidder, bob.account_id()); - // Check that Alice was returned her bid - let new_alice_balance = alice.view_account().await?.balance; + // Check that Alice was refunded her bid + let new_alice_balance = alice + .tokens() + .near_balance() + .fetch_from(&sandbox_network) + .await? + .total; assert!(new_alice_balance == alice_balance.saturating_add(NearToken::from_near(1))); // Alice tries to make a bid with less NEAR than the previous - let alice_bid = alice - .call(contract.id(), "bid") + contract + .call_function("bid", ()) + .transaction() .deposit(NearToken::from_near(1)) - .transact() - .await?; - - assert!(alice_bid.is_failure()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Auctioneer claims auction but did not finish - let auctioneer_claim = auctioneer - .call(contract_account.id(), "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - - assert!(auctioneer_claim.is_failure()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(30)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Fast forward 200 blocks let blocks_to_advance = 200; sandbox.fast_forward(blocks_to_advance).await?; - let auctioneer_claim = auctioneer - .call(contract_account.id(), "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - - assert!(auctioneer_claim.is_success()); + // Auctioneer claims the auction + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(40)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Checks the auctioneer has the correct balance - let auctioneer_balance = auctioneer.view_account().await?.balance; + let auctioneer_balance = auctioneer + .tokens() + .near_balance() + .fetch_from(&sandbox_network) + .await? + .total; assert!(auctioneer_balance <= NearToken::from_near(12)); assert!(auctioneer_balance > NearToken::from_millinear(11990)); // Check highest bidder received the NFT let token_info: serde_json::Value = nft_contract - .call("nft_token") - .args_json(json!({"token_id": TOKEN_ID})) - .transact() + .call_function("nft_token", json!({"token_id": TOKEN_ID})) + .read_only() + .fetch_from(&sandbox_network) .await? - .json() - .unwrap(); + .data; let owner_id: String = token_info["owner_id"].as_str().unwrap().to_string(); assert_eq!( owner_id, - bob.id().to_string(), + bob.account_id().to_string(), "token owner is not the highest bidder" ); // Auctioneer tries to claim the auction again - let auctioneer_claim = auctioneer - .call(contract_account.id(), "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - - assert!(auctioneer_claim.is_failure()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(30)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Alice tries to make a bid when the auction is over - let alice_bid = alice - .call(contract.id(), "bid") - .deposit(NearToken::from_near(3)) - .transact() - .await?; - - assert!(alice_bid.is_failure()); + contract + .call_function("bid", ()) + .transaction() + .deposit(NearToken::from_near(1)) + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); Ok(()) } async fn create_subaccount( - root: &near_workspaces::Account, + sandbox: &near_sandbox::Sandbox, name: &str, -) -> Result> { - let subaccount = root - .create_subaccount(name) - .initial_balance(TEN_NEAR) - .transact() - .await? - .unwrap(); - - Ok(subaccount) +) -> testresult::TestResult { + let account_id: AccountId = name.parse().unwrap(); + sandbox + .create_account(account_id.clone()) + .initial_balance(NearToken::from_near(10)) + .send() + .await?; + Ok(near_api::Account(account_id)) } diff --git a/contract-rs/03-bid-with-fts/Cargo.toml b/contract-rs/03-bid-with-fts/Cargo.toml index 3902e2ca..595251a6 100644 --- a/contract-rs/03-bid-with-fts/Cargo.toml +++ b/contract-rs/03-bid-with-fts/Cargo.toml @@ -9,14 +9,20 @@ crate-type = ["cdylib", "rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -near-sdk = "5.23.0" +near-sdk = "5.24.0" [dev-dependencies] -near-sdk = { version = "5.23.0", features = ["unit-testing"] } -near-workspaces = { version = "0.22.0", features = ["unstable"] } +near-sdk = { version = "5.24.0", features = ["unit-testing"] } +near-sandbox = "0.3" +near-api = "0.8" +cargo-near-build = "0.10" tokio = { version = "1.12.0", features = ["full"] } serde_json = "1" -chrono = "0.4.38" +testresult = "0.4.1" +# This is temporary fix for the build error since those crates with a higher version require a higher version of Rust compiler (1.88.0) +cargo-platform = "=0.3.1" +darling = "=0.20.11" +bon = "=3.8.1" [profile.release] codegen-units = 1 diff --git a/contract-rs/03-bid-with-fts/tests/test_basics.rs b/contract-rs/03-bid-with-fts/tests/test_basics.rs index bf3d2942..a428acdc 100644 --- a/contract-rs/03-bid-with-fts/tests/test_basics.rs +++ b/contract-rs/03-bid-with-fts/tests/test_basics.rs @@ -1,66 +1,83 @@ -use chrono::Utc; +use near_api::{AccountId, NearGas, NearToken}; use near_sdk::json_types::U128; -use near_sdk::near; -use near_workspaces::result::ExecutionFinalResult; -use near_workspaces::types::{AccountId, Gas, NearToken}; -use near_workspaces::{Account, Contract}; -use serde_json::json; - -#[near(serializers = [json])] -#[derive(Clone)] +use near_sdk::serde_json::json; + +#[derive(near_sdk::serde::Deserialize)] +#[serde(crate = "near_sdk::serde")] pub struct Bid { pub bidder: AccountId, pub bid: U128, } -const TEN_NEAR: NearToken = NearToken::from_near(10); const FT_WASM_FILEPATH: &str = "./tests/fungible_token.wasm"; const NFT_WASM_FILEPATH: &str = "./tests/non_fungible_token.wasm"; #[tokio::test] -async fn test_contract_is_operational() -> Result<(), Box> { - let sandbox = near_workspaces::sandbox().await?; - - let root: near_workspaces::Account = sandbox.root_account()?; - - // Create accounts - let alice = create_subaccount(&root, "alice").await?; - let bob = create_subaccount(&root, "bob").await?; - let auctioneer = create_subaccount(&root, "auctioneer").await?; - let contract_account = create_subaccount(&root, "contract").await?; +async fn test_contract_is_operational() -> testresult::TestResult<()> { + // Build the contract wasm file + let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?; + let contract_wasm = std::fs::read(contract_wasm_path)?; + // Read the NFT and FT wasm file + let nft_wasm = std::fs::read(NFT_WASM_FILEPATH)?; let ft_wasm = std::fs::read(FT_WASM_FILEPATH)?; - let ft_contract = sandbox.dev_deploy(&ft_wasm).await?; - - // Initialize FT contract - let res = ft_contract - .call("new_default_meta") - .args_json(serde_json::json!({ - "owner_id": root.id(), - "total_supply": U128(1_000_000), - })) - .transact() - .await?; - - assert!(res.is_success()); - // Deploy and initialize NFT contract - let nft_wasm = std::fs::read(NFT_WASM_FILEPATH)?; - let nft_contract = sandbox.dev_deploy(&nft_wasm).await?; + // Initialize the sandbox + let sandbox = near_sandbox::Sandbox::start_sandbox().await?; + let sandbox_network = + near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?); - let res = nft_contract - .call("new_default_meta") - .args_json(serde_json::json!({"owner_id": root.id()})) - .transact() - .await?; - - assert!(res.is_success()); + // Create accounts + let alice = create_subaccount(&sandbox, "alice.sandbox").await?; + let bob = create_subaccount(&sandbox, "bob.sandbox").await?; + let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; + let nft_contract = create_subaccount(&sandbox, "nft-contract.sandbox") + .await? + .as_contract(); + let ft_contract = create_subaccount(&sandbox, "ft-contract.sandbox") + .await? + .as_contract(); + let contract = create_subaccount(&sandbox, "contract.sandbox") + .await? + .as_contract(); + + // Initialize signer for the contract deployment + let signer = near_api::Signer::from_secret_key( + near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY + .parse() + .unwrap(), + )?; + + // Deploy the NFT contract + near_api::Contract::deploy(nft_contract.account_id().clone()) + .use_code(nft_wasm) + .with_init_call( + "new_default_meta", + json!({"owner_id": nft_contract.account_id()}), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); + + // Deploy the FT contract + near_api::Contract::deploy(ft_contract.account_id().clone()) + .use_code(ft_wasm) + .with_init_call( + "new_default_meta", + json!({"owner_id": ft_contract.account_id(), "total_supply": U128(1_000_000)}), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Mint NFT + const TOKEN_ID: &str = "1"; let request_payload = json!({ - "token_id": "1", - "receiver_id": contract_account.id(), + "token_id": TOKEN_ID, + "receiver_id": contract.account_id(), "token_metadata": { "title": "LEEROYYYMMMJENKINSSS", "description": "Alright time's up, let's do this.", @@ -68,283 +85,374 @@ async fn test_contract_is_operational() -> Result<(), Box }, }); - let res = contract_account - .call(nft_contract.id(), "nft_mint") - .args_json(request_payload) + nft_contract + .call_function("nft_mint", request_payload) + .transaction() .deposit(NearToken::from_millinear(80)) - .transact() - .await?; - - assert!(res.is_success()); + .with_signer(nft_contract.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Register accounts for account in [ alice.clone(), bob.clone(), - contract_account.clone(), + contract.as_account().clone(), auctioneer.clone(), ] .iter() { - let register = account - .call(ft_contract.id(), "storage_deposit") - .args_json(serde_json::json!({ "account_id": account.id() })) + ft_contract + .call_function( + "storage_deposit", + serde_json::json!({ "account_id": account.account_id().clone() }), + ) + .transaction() .deposit(NearToken::from_yoctonear(8000000000000000000000)) - .transact() - .await?; - - assert!(register.is_success()); + .with_signer(account.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); } - // Transfer FTs + // Transfer FTs to Alice and Bob to top up their balances let transfer_amount = U128(150_000); - let root_transfer_alice = - ft_transfer(&root, alice.clone(), ft_contract.clone(), transfer_amount).await?; - assert!(root_transfer_alice.is_success()); - let root_transfer_bob = - ft_transfer(&root, bob.clone(), ft_contract.clone(), transfer_amount).await?; - assert!(root_transfer_bob.is_success()); + ft_transfer( + &ft_contract, + &ft_contract.account_id(), + alice.account_id(), + transfer_amount, + &signer, + &sandbox_network, + ) + .await?; - // Deploy and initialize auction contract - let contract_wasm = near_workspaces::compile_project("./").await?; - let contract = contract_account.deploy(&contract_wasm).await?.unwrap(); + ft_transfer( + &ft_contract, + &ft_contract.account_id(), + bob.account_id(), + transfer_amount, + &signer, + &sandbox_network, + ) + .await?; - let now = Utc::now().timestamp(); + // Deploy and initialize auction contract + let now = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(); let a_minute_from_now = (now + 60) * 1000000000; let starting_price = U128(10_000); - let init: ExecutionFinalResult = contract - .call("init") - .args_json( - json!({"end_time": a_minute_from_now.to_string(),"auctioneer": auctioneer.id(),"ft_contract": ft_contract.id(),"nft_contract":nft_contract.id(),"token_id":"1", "starting_price":starting_price }), - ) - .transact() - .await?; - - assert!(init.is_success()); + near_api::Contract::deploy(contract.account_id().clone()) + .use_code(contract_wasm) + .with_init_call( + "init", + serde_json::json!({ + "end_time": a_minute_from_now.to_string(), + "auctioneer": auctioneer.account_id(), + "ft_contract": ft_contract.account_id(), + "nft_contract": nft_contract.account_id(), + "token_id": "1", + "starting_price": starting_price + }), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Alice makes bid less than starting price - let alice_bid = ft_transfer_call( - alice.clone(), - ft_contract.id(), - contract_account.id(), + ft_transfer_call( + &ft_contract, + &alice, + contract.account_id(), U128(5_000), + &signer, + &sandbox_network, ) .await?; - assert!(alice_bid.is_success()); - - let highest_bid_alice: Bid = contract.view("get_highest_bid").await?.json()?; - assert_eq!(highest_bid_alice.bid, U128(10_000)); - assert_eq!(highest_bid_alice.bidder, *contract.id()); + // For now, the highest bid is the Alice's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; + assert_eq!(highest_bid.bid, U128(10_000)); + assert_eq!(&highest_bid.bidder, contract.account_id()); - let contract_account_balance: U128 = ft_balance_of(&ft_contract, contract_account.id()).await?; - assert_eq!(contract_account_balance, U128(0)); + // Contract balance has not changed yet + let contract_balance: U128 = + ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; + assert_eq!(contract_balance, U128(0)); - let alice_balance_after_bid: U128 = ft_balance_of(&ft_contract, alice.id()).await?; - assert_eq!(alice_balance_after_bid, U128(150_000)); + // Alice balance has not changed yet + let alice_balance: U128 = + ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; + assert_eq!(alice_balance, U128(150_000)); // Alice makes valid bid - let alice_bid = ft_transfer_call( - alice.clone(), - ft_contract.id(), - contract_account.id(), + ft_transfer_call( + &ft_contract, + &alice, + contract.account_id(), U128(50_000), + &signer, + &sandbox_network, ) .await?; - assert!(alice_bid.is_success()); + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; + assert_eq!(highest_bid.bid, U128(50_000)); + assert_eq!(&highest_bid.bidder, alice.account_id()); - let highest_bid_alice: Bid = contract.view("get_highest_bid").await?.json()?; - assert_eq!(highest_bid_alice.bid, U128(50_000)); - assert_eq!(highest_bid_alice.bidder, *alice.id()); + // Contract balance has increased + let contract_balance: U128 = + ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; + assert_eq!(contract_balance, U128(50_000)); - let contract_account_balance: U128 = ft_balance_of(&ft_contract, contract_account.id()).await?; - assert_eq!(contract_account_balance, U128(50_000)); - let alice_balance_after_bid: U128 = ft_balance_of(&ft_contract, alice.id()).await?; - assert_eq!(alice_balance_after_bid, U128(100_000)); + // Alice balance has decreased + let alice_balance: U128 = + ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; + assert_eq!(alice_balance, U128(100_000)); // Bob makes a higher bid - let bob_bid = ft_transfer_call( - bob.clone(), - ft_contract.id(), - contract_account.id(), + ft_transfer_call( + &ft_contract, + &bob, + contract.account_id(), U128(60_000), + &signer, + &sandbox_network, ) .await?; - assert!(bob_bid.is_success()); - - let highest_bid: Bid = contract.view("get_highest_bid").await?.json()?; + // The highest bid is now Bob's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; assert_eq!(highest_bid.bid, U128(60_000)); - assert_eq!(highest_bid.bidder, *bob.id()); + assert_eq!(&highest_bid.bidder, bob.account_id()); - // Checks Alice was returned her bid - let alice_balance_after_bid: U128 = ft_balance_of(&ft_contract, alice.id()).await?; - assert_eq!(alice_balance_after_bid, U128(150_000)); - let bob_balance_after_bid: U128 = ft_balance_of(&ft_contract, bob.id()).await?; - assert_eq!(bob_balance_after_bid, U128(90_000)); + // Checks Alice was refunded her bid + let alice_balance: U128 = + ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; + assert_eq!(alice_balance, U128(150_000)); + + // Bob balance has decreased + let bob_balance: U128 = ft_balance_of(&ft_contract, bob.account_id(), &sandbox_network).await?; + assert_eq!(bob_balance, U128(90_000)); // Alice tries to make a bid with less FTs than the previous - let alice_bid: ExecutionFinalResult = ft_transfer_call( - alice.clone(), - ft_contract.id(), - contract_account.id(), + ft_transfer_call( + &ft_contract, + &alice, + contract.account_id(), U128(50_000), + &signer, + &sandbox_network, ) .await?; - assert!(alice_bid.is_success()); + // The highest bid is still Bob's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; + assert_eq!(highest_bid.bid, U128(60_000)); + assert_eq!(&highest_bid.bidder, bob.account_id()); - let highest_bid_alice: Bid = contract.view("get_highest_bid").await?.json()?; - assert_eq!(highest_bid_alice.bid, U128(60_000)); - assert_eq!(highest_bid_alice.bidder, *bob.id()); + // Contract balance has not changed + let contract_balance: U128 = + ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; + assert_eq!(contract_balance, U128(60_000)); - let contract_account_balance: U128 = ft_balance_of(&ft_contract, contract_account.id()).await?; - assert_eq!(contract_account_balance, U128(60_000)); - let alice_balance_after_bid: U128 = ft_balance_of(&ft_contract, alice.id()).await?; - assert_eq!(alice_balance_after_bid, U128(150_000)); + // Alice balance has not changed + let alice_balance: U128 = + ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; + assert_eq!(alice_balance, U128(150_000)); // Auctioneer claims auction but did not finish - let auctioneer_claim: ExecutionFinalResult = - claim(auctioneer.clone(), contract_account.id()).await?; - - assert!(auctioneer_claim.is_failure()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(300)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Fast forward 200 blocks let blocks_to_advance = 200; sandbox.fast_forward(blocks_to_advance).await?; // Auctioneer claims auction - let auctioneer_claim: ExecutionFinalResult = - claim(auctioneer.clone(), contract_account.id()).await?; + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(300)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - assert!(auctioneer_claim.is_success()); + // Contract balance has been cleared + let contract_balance: U128 = + ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; + assert_eq!(contract_balance, U128(0)); - let contract_account_balance: U128 = ft_balance_of(&ft_contract, contract_account.id()).await?; - assert_eq!(contract_account_balance, U128(0)); - let auctioneer_balance_after_claim: U128 = ft_balance_of(&ft_contract, auctioneer.id()).await?; - assert_eq!(auctioneer_balance_after_claim, U128(60_000)); + // Auctioneer balance has increased + let auctioneer_balance: U128 = + ft_balance_of(&ft_contract, auctioneer.account_id(), &sandbox_network).await?; + assert_eq!(auctioneer_balance, U128(60_000)); // Check highest bidder received the NFT let token_info: serde_json::Value = nft_contract - .call("nft_token") - .args_json(json!({"token_id": "1"})) - .transact() + .call_function("nft_token", serde_json::json!({"token_id": "1"})) + .read_only() + .fetch_from(&sandbox_network) .await? - .json() - .unwrap(); + .data; let owner_id: String = token_info["owner_id"].as_str().unwrap().to_string(); assert_eq!( owner_id, - bob.id().to_string(), + bob.account_id().to_string(), "token owner is not the highest bidder" ); // Auctioneer claims auction back but fails - let auctioneer_claim: ExecutionFinalResult = - claim(auctioneer.clone(), contract_account.id()).await?; - - assert!(auctioneer_claim.is_failure()); + contract + .call_function("claim", ()) + .transaction() + .gas(NearGas::from_tgas(300)) + .with_signer(auctioneer.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); // Alice tries to make a bid when the auction is over - let alice_bid: ExecutionFinalResult = ft_transfer_call( - alice.clone(), - ft_contract.id(), - contract_account.id(), + ft_transfer_call( + &ft_contract, + &alice, + contract.account_id(), U128(70_000), + &signer, + &sandbox_network, ) .await?; - assert!(alice_bid.is_success()); + // The highest bid is still Bob's bid + let highest_bid: Bid = contract + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) + .await? + .data; + assert_eq!(highest_bid.bid, U128(60_000)); + assert_eq!(&highest_bid.bidder, bob.account_id()); - let highest_bid_alice: Bid = contract.view("get_highest_bid").await?.json()?; - assert_eq!(highest_bid_alice.bid, U128(60_000)); - assert_eq!(highest_bid_alice.bidder, *bob.id()); + // Contract balance has not changed + let contract_balance: U128 = + ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; + assert_eq!(contract_balance, U128(0)); - let contract_account_balance: U128 = ft_balance_of(&ft_contract, contract_account.id()).await?; - assert_eq!(contract_account_balance, U128(0)); - let alice_balance_after_bid: U128 = ft_balance_of(&ft_contract, alice.id()).await?; - assert_eq!(alice_balance_after_bid, U128(150_000)); - let bob_balance_after_bid: U128 = ft_balance_of(&ft_contract, bob.id()).await?; - assert_eq!(bob_balance_after_bid, U128(90_000)); + // Alice balance has not changed + let alice_balance: U128 = + ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; + assert_eq!(alice_balance, U128(150_000)); + + // Bob balance has not changed + let bob_balance: U128 = ft_balance_of(&ft_contract, bob.account_id(), &sandbox_network).await?; + assert_eq!(bob_balance, U128(90_000)); Ok(()) } async fn create_subaccount( - root: &near_workspaces::Account, + sandbox: &near_sandbox::Sandbox, name: &str, -) -> Result> { - let subaccount = root - .create_subaccount(name) - .initial_balance(TEN_NEAR) - .transact() - .await? - .unwrap(); - - Ok(subaccount) +) -> testresult::TestResult { + let account_id: AccountId = name.parse().unwrap(); + sandbox + .create_account(account_id.clone()) + .initial_balance(NearToken::from_near(10)) + .send() + .await?; + Ok(near_api::Account(account_id)) } async fn ft_transfer( - root: &near_workspaces::Account, - account: Account, - ft_contract: Contract, - transfer_amount: U128, -) -> Result> { - let transfer = root - .call(ft_contract.id(), "ft_transfer") - .args_json(serde_json::json!({ - "receiver_id": account.id(), - "amount": transfer_amount - })) + ft_contract: &near_api::Contract, + from: &AccountId, + to: &AccountId, + amount: U128, + signer: &std::sync::Arc, + network: &near_api::NetworkConfig, +) -> testresult::TestResult<()> { + ft_contract + .call_function( + "ft_transfer", + serde_json::json!({"receiver_id": to, "amount": amount}), + ) + .transaction() .deposit(NearToken::from_yoctonear(1)) - .transact() - .await?; - Ok(transfer) + .with_signer(from.clone(), signer.clone()) + .send_to(network) + .await? + .assert_success(); + Ok(()) } async fn ft_balance_of( - ft_contract: &Contract, + ft_contract: &near_api::Contract, account_id: &AccountId, -) -> Result> { - let result = ft_contract - .view("ft_balance_of") - .args_json(json!({"account_id": account_id})) + network: &near_api::NetworkConfig, +) -> testresult::TestResult { + let result: U128 = ft_contract + .call_function( + "ft_balance_of", + serde_json::json!({"account_id": account_id}), + ) + .read_only() + .fetch_from(network) .await? - .json()?; - + .data; Ok(result) } async fn ft_transfer_call( - account: Account, - ft_contract_id: &AccountId, + ft_contract: &near_api::Contract, + account: &near_api::Account, receiver_id: &AccountId, amount: U128, -) -> Result> { - let transfer = account - .call(ft_contract_id, "ft_transfer_call") - .args_json(serde_json::json!({ - "receiver_id": receiver_id, "amount":amount, "msg": "0" })) + signer: &std::sync::Arc, + network: &near_api::NetworkConfig, +) -> testresult::TestResult<()> { + let _ = ft_contract + .call_function( + "ft_transfer_call", + serde_json::json!({"receiver_id": receiver_id, "amount": amount, "msg": "0"}), + ) + .transaction() .deposit(NearToken::from_yoctonear(1)) - .gas(Gas::from_tgas(300)) - .transact() + .gas(NearGas::from_tgas(300)) + .with_signer(account.account_id().clone(), signer.clone()) + .send_to(network) .await?; - Ok(transfer) -} - -async fn claim( - account: Account, - contract_id: &AccountId, -) -> Result> { - let claim: ExecutionFinalResult = account - .call(contract_id, "claim") - .args_json(json!({})) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - Ok(claim) + Ok(()) } diff --git a/factory/Cargo.toml b/factory/Cargo.toml index 31460c10..992e37b3 100644 --- a/factory/Cargo.toml +++ b/factory/Cargo.toml @@ -13,14 +13,20 @@ crate-type = ["cdylib", "rlib"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -near-sdk = "5.23.0" +near-sdk = "5.24.0" [dev-dependencies] -near-sdk = { version = "5.23.0", features = ["unit-testing"] } -near-workspaces = { version = "0.22.0", features = ["unstable"] } +near-sdk = { version = "5.24.0", features = ["unit-testing"] } +near-sandbox = "0.3" +near-api = "0.8" +cargo-near-build = "0.10" tokio = { version = "1.12.0", features = ["full"] } serde_json = "1" -chrono = "0.4.38" +testresult = "0.4.1" +# This is temporary fix for the build error since those crates with a higher version require a higher version of Rust compiler (1.88.0) +cargo-platform = "=0.3.1" +darling = "=0.20.11" +bon = "=3.8.1" [profile.release] codegen-units = 1 diff --git a/factory/tests/tests_basics.rs b/factory/tests/tests_basics.rs index 21c97733..37fbed06 100644 --- a/factory/tests/tests_basics.rs +++ b/factory/tests/tests_basics.rs @@ -1,14 +1,11 @@ -use chrono::Utc; -use near_sdk::{json_types::U128, NearToken}; -use near_sdk::{near, AccountId, Gas}; -use near_workspaces::result::ExecutionFinalResult; -use near_workspaces::{Account, Contract}; -use serde_json::json; - -const TEN_NEAR: NearToken = NearToken::from_near(10); +use near_api::{AccountId, NearGas, NearToken}; +use near_sdk::json_types::U128; +use near_sdk::serde_json::json; + const FT_WASM_FILEPATH: &str = "./tests/fungible_token.wasm"; -#[near(serializers = [json])] +#[derive(near_sdk::serde::Deserialize)] +#[serde(crate = "near_sdk::serde")] pub struct Bid { pub bidder: AccountId, pub bid: U128, @@ -16,174 +13,234 @@ pub struct Bid { #[tokio::test] -async fn test_contract_is_operational() -> Result<(), Box> { - let sandbox = near_workspaces::sandbox().await?; - - let root: near_workspaces::Account = sandbox.root_account()?; - - // Create accounts - let alice = create_subaccount(&root, "alice").await?; - let auctioneer = create_subaccount(&root, "auctioneer").await?; - let contract_account = create_subaccount(&root, "contract").await?; - let nft_account = create_subaccount(&root, "nft").await?; +async fn test_contract_is_operational() -> testresult::TestResult<()> { + // Build the contract wasm file + let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?; + let contract_wasm = std::fs::read(contract_wasm_path)?; + // Read the FT wasm file let ft_wasm = std::fs::read(FT_WASM_FILEPATH)?; - let ft_contract = sandbox.dev_deploy(&ft_wasm).await?; - - // Initialize FT contract - let res = ft_contract - .call("new_default_meta") - .args_json(serde_json::json!({ - "owner_id": root.id(), - "total_supply": U128(1_000_000), - })) - .transact() - .await?; - assert!(res.is_success()); + // Initialize the sandbox + let sandbox = near_sandbox::Sandbox::start_sandbox().await?; + let sandbox_network = + near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?); + + // Create accounts + let alice = create_subaccount(&sandbox, "alice.sandbox").await?; + let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; + let nft_contract = create_subaccount(&sandbox, "nft-contract.sandbox") + .await? + .as_contract(); + let ft_contract = create_subaccount(&sandbox, "ft-contract.sandbox") + .await? + .as_contract(); + let contract = create_subaccount(&sandbox, "contract.sandbox") + .await? + .as_contract(); + + // Initialize signer for the contract deployment + let signer = near_api::Signer::from_secret_key( + near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY + .parse() + .unwrap(), + )?; + + // Deploy the FT contract + near_api::Contract::deploy(ft_contract.account_id().clone()) + .use_code(ft_wasm) + .with_init_call( + "new_default_meta", + json!({"owner_id": ft_contract.account_id(), "total_supply": U128(1_000_000)}), + )? + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Skip creating NFT contract as we are are only going to test making a bid to the auction // Deploy factory contract - let contract_wasm = near_workspaces::compile_project("./").await?; - let contract = contract_account.deploy(&contract_wasm).await?.unwrap(); + near_api::Contract::deploy(contract.account_id().clone()) + .use_code(contract_wasm) + .without_init_call() + .with_signer(signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); // Create auction by calling factory contract - let now = Utc::now().timestamp(); + let now = std::time::SystemTime::now() + .duration_since(std::time::SystemTime::UNIX_EPOCH)? + .as_secs(); let a_minute_from_now = (now + 60) * 1000000000; let starting_price = U128(10_000); - let deploy_new_auction: ExecutionFinalResult = alice - .call(contract.id(), "deploy_new_auction") - .args_json( - json!({"name": "new-auction", "end_time": a_minute_from_now.to_string(),"auctioneer": auctioneer.id(),"ft_contract": ft_contract.id(),"nft_contract": nft_account.id(),"token_id":"1", "starting_price":starting_price }), - ) - .max_gas() + contract + .call_function("deploy_new_auction", json!({"name": "new-auction", "end_time": a_minute_from_now.to_string(),"auctioneer": auctioneer.account_id(),"ft_contract": ft_contract.account_id(),"nft_contract": nft_contract.account_id(),"token_id":"1", "starting_price":starting_price }),) + .transaction() .deposit(NearToken::from_millinear(1600)) - .transact() - .await?; - - assert!(deploy_new_auction.is_success()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); - let auction_account_id: AccountId = format!("new-auction.{}", contract.id()).parse().unwrap(); + let auction_account_id: AccountId = format!("new-auction.{}", contract.account_id()) + .parse() + .unwrap(); + sandbox + .import_account( + sandbox.rpc_addr.to_string(), + format!("new-auction.{}", contract.account_id()) + .parse() + .unwrap(), + ) + .send() + .await?; + let auction_account = near_api::Account(auction_account_id.clone()).as_contract(); // Register accounts - for account_id in [alice.id().clone(), auction_account_id.clone()].iter() { - let register = ft_contract - .call("storage_deposit") - .args_json(serde_json::json!({ "account_id": account_id })) + for account_id in [alice.account_id().clone(), auction_account_id.clone()].iter() { + ft_contract + .call_function( + "storage_deposit", + serde_json::json!({ "account_id": account_id }), + ) + .transaction() .deposit(NearToken::from_yoctonear(8000000000000000000000)) - .transact() - .await?; - - assert!(register.is_success()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_success(); } - // Transfer FTs + // Transfer FTs to Alice and Bob to top up their balances let transfer_amount = U128(150_000); - let root_transfer_alice = - ft_transfer(&root, alice.clone(), ft_contract.clone(), transfer_amount).await?; - assert!(root_transfer_alice.is_success()); + ft_transfer( + &ft_contract, + &ft_contract.account_id(), + alice.account_id(), + transfer_amount, + &signer, + &sandbox_network, + ) + .await?; // Alice makes a bid - let alice_bid = ft_transfer_call( - alice.clone(), - ft_contract.id(), + ft_transfer_call( + &ft_contract, + &alice, &auction_account_id, U128(50_000), + &signer, + &sandbox_network, ) .await?; - assert!(alice_bid.is_success()); - - let highest_bid_alice: Bid = alice - .view(&auction_account_id, "get_highest_bid") - .args_json({}) + let highest_bid: Bid = auction_account + .call_function("get_highest_bid", ()) + .read_only() + .fetch_from(&sandbox_network) .await? - .json()?; - assert_eq!(highest_bid_alice.bid, U128(50_000)); - assert_eq!(highest_bid_alice.bidder, *alice.id()); + .data; + assert_eq!(highest_bid.bid, U128(50_000)); + assert_eq!(&highest_bid.bidder, alice.account_id()); + + // Contract balance has increased + let auction_contract_balance: U128 = + ft_balance_of(&ft_contract, auction_account.account_id(), &sandbox_network).await?; + assert_eq!(auction_contract_balance, U128(50_000)); - let contract_account_balance: U128 = ft_balance_of(&ft_contract, &auction_account_id).await?; - assert_eq!(contract_account_balance, U128(50_000)); - let alice_balance_after_bid: U128 = ft_balance_of(&ft_contract, alice.id()).await?; - assert_eq!(alice_balance_after_bid, U128(100_000)); + // Alice balance has decreased + let alice_balance: U128 = + ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; + assert_eq!(alice_balance, U128(100_000)); // Try to launch a new auction with insufficient deposit - let deploy_new_auction: ExecutionFinalResult = alice - .call(contract.id(), "deploy_new_auction") - .args_json( - json!({"name": "new-auction", "end_time": a_minute_from_now.to_string(),"auctioneer": auctioneer.id(),"ft_contract": ft_contract.id(),"nft_contract": nft_account.id(),"token_id":"1", "starting_price":starting_price }), - ) - .max_gas() + contract + .call_function("deploy_new_auction", json!({"name": "new-auction", "end_time": a_minute_from_now.to_string(),"auctioneer": auctioneer.account_id(),"ft_contract": ft_contract.account_id(),"nft_contract": nft_contract.account_id(),"token_id":"1", "starting_price":starting_price }),) + .transaction() .deposit(NearToken::from_millinear(1400)) - .transact() - .await?; - - assert!(deploy_new_auction.is_failure()); + .with_signer(alice.account_id().clone(), signer.clone()) + .send_to(&sandbox_network) + .await? + .assert_failure(); Ok(()) } async fn create_subaccount( - root: &near_workspaces::Account, + sandbox: &near_sandbox::Sandbox, name: &str, -) -> Result> { - let subaccount = root - .create_subaccount(name) - .initial_balance(TEN_NEAR) - .transact() - .await? - .unwrap(); - - Ok(subaccount) +) -> testresult::TestResult { + let account_id: AccountId = name.parse().unwrap(); + sandbox + .create_account(account_id.clone()) + .initial_balance(NearToken::from_near(10)) + .send() + .await?; + Ok(near_api::Account(account_id)) } async fn ft_transfer( - root: &near_workspaces::Account, - account: Account, - ft_contract: Contract, - transfer_amount: U128, -) -> Result> { - let transfer = root - .call(ft_contract.id(), "ft_transfer") - .args_json(serde_json::json!({ - "receiver_id": account.id(), - "amount": transfer_amount - })) + ft_contract: &near_api::Contract, + from: &AccountId, + to: &AccountId, + amount: U128, + signer: &std::sync::Arc, + network: &near_api::NetworkConfig, +) -> testresult::TestResult<()> { + ft_contract + .call_function( + "ft_transfer", + serde_json::json!({"receiver_id": to, "amount": amount}), + ) + .transaction() .deposit(NearToken::from_yoctonear(1)) - .transact() - .await?; - Ok(transfer) + .with_signer(from.clone(), signer.clone()) + .send_to(network) + .await? + .assert_success(); + Ok(()) } async fn ft_balance_of( - ft_contract: &Contract, + ft_contract: &near_api::Contract, account_id: &AccountId, -) -> Result> { - let result = ft_contract - .view("ft_balance_of") - .args_json(json!({"account_id": account_id})) + network: &near_api::NetworkConfig, +) -> testresult::TestResult { + let result: U128 = ft_contract + .call_function( + "ft_balance_of", + serde_json::json!({"account_id": account_id}), + ) + .read_only() + .fetch_from(network) .await? - .json()?; - + .data; Ok(result) } async fn ft_transfer_call( - account: Account, - ft_contract_id: &AccountId, + ft_contract: &near_api::Contract, + account: &near_api::Account, receiver_id: &AccountId, amount: U128, -) -> Result> { - let transfer = account - .call(ft_contract_id, "ft_transfer_call") - .args_json(serde_json::json!({ - "receiver_id": receiver_id, "amount":amount, "msg": "0" })) + signer: &std::sync::Arc, + network: &near_api::NetworkConfig, +) -> testresult::TestResult<()> { + let _ = ft_contract + .call_function( + "ft_transfer_call", + serde_json::json!({"receiver_id": receiver_id, "amount": amount, "msg": "0"}), + ) + .transaction() .deposit(NearToken::from_yoctonear(1)) - .gas(Gas::from_tgas(300)) - .transact() - .await?; - Ok(transfer) + .gas(NearGas::from_tgas(300)) + .with_signer(account.account_id().clone(), signer.clone()) + .send_to(network) + .await? + .assert_success(); + Ok(()) }