From 3d6137eaa5dab0dfbeb88902b039883af474249b Mon Sep 17 00:00:00 2001 From: PiVortex Date: Fri, 4 Oct 2024 14:06:35 +0700 Subject: [PATCH 01/11] reorg contracts + change auctioneer to accountid in ts --- .github/workflows/tests-rs-02.yml | 2 +- .github/workflows/tests-rs-03.yml | 2 +- .github/workflows/tests-rs-04.yml | 14 -- .github/workflows/tests-ts-02.yml | 2 +- .github/workflows/tests-ts-03.yml | 2 +- .github/workflows/tests-ts-04.yml | 19 --- contract-rs/01-basic-auction/Cargo.toml | 2 +- contract-rs/01-basic-auction/src/lib.rs | 47 ++++-- .../01-basic-auction/tests/test_basics.rs | 48 +++++- contract-rs/02-owner-claims-money/Cargo.toml | 27 ---- contract-rs/02-owner-claims-money/src/lib.rs | 110 ------------- .../tests/test_basics.rs | 148 ------------------ .../Cargo.toml | 0 .../README.md | 0 .../rust-toolchain.toml | 0 .../src/ext.rs | 0 .../src/lib.rs | 0 .../tests/non_fungible_token.wasm | Bin .../tests/test_basics.rs | 0 .../Cargo.toml | 0 .../README.md | 0 .../rust-toolchain.toml | 0 .../src/ext.rs | 0 .../src/lib.rs | 0 .../tests/fungible_token.wasm | Bin .../tests/non_fungible_token.wasm | Bin .../tests/test_basics.rs | 0 .../README.md | 39 ----- .../rust-toolchain.toml | 4 - contract-ts/01-basic-auction/package.json | 2 +- .../01-basic-auction/sandbox-test/main.ava.js | 30 +++- contract-ts/01-basic-auction/src/contract.ts | 25 ++- .../sandbox-test/main.ava.js | 88 ----------- .../02-owner-claims-money/src/contract.ts | 62 -------- .../README.md | 0 .../package.json | 0 .../sandbox-test/main.ava.js | 0 .../sandbox-test/non_fungible_token.wasm | Bin .../src/contract.ts | 4 +- .../tsconfig.json | 0 .../README.md | 0 .../package.json | 0 .../sandbox-test/fungible_token.wasm | Bin .../sandbox-test/main.ava.js | 0 .../sandbox-test/non_fungible_token.wasm | Bin .../src/contract.ts | 4 +- .../tsconfig.json | 0 .../README.md | 83 ---------- .../package.json | 22 --- .../tsconfig.json | 14 -- 50 files changed, 137 insertions(+), 663 deletions(-) delete mode 100644 .github/workflows/tests-rs-04.yml delete mode 100644 .github/workflows/tests-ts-04.yml delete mode 100644 contract-rs/02-owner-claims-money/Cargo.toml delete mode 100644 contract-rs/02-owner-claims-money/src/lib.rs delete mode 100644 contract-rs/02-owner-claims-money/tests/test_basics.rs rename contract-rs/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/Cargo.toml (100%) rename contract-rs/{02-owner-claims-money => 02-winner-gets-nft}/README.md (100%) rename contract-rs/{02-owner-claims-money => 02-winner-gets-nft}/rust-toolchain.toml (100%) rename contract-rs/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/src/ext.rs (100%) rename contract-rs/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/src/lib.rs (100%) rename contract-rs/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/tests/non_fungible_token.wasm (100%) rename contract-rs/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/tests/test_basics.rs (100%) rename contract-rs/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/Cargo.toml (100%) rename contract-rs/{03-owner-claims-winner-gets-nft => 03-bid-with-fts}/README.md (100%) rename contract-rs/{03-owner-claims-winner-gets-nft => 03-bid-with-fts}/rust-toolchain.toml (100%) rename contract-rs/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/src/ext.rs (100%) rename contract-rs/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/src/lib.rs (100%) rename contract-rs/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/tests/fungible_token.wasm (100%) rename contract-rs/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/tests/non_fungible_token.wasm (100%) rename contract-rs/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/tests/test_basics.rs (100%) delete mode 100644 contract-rs/04-ft-owner-claims-winner-gets-nft/README.md delete mode 100644 contract-rs/04-ft-owner-claims-winner-gets-nft/rust-toolchain.toml delete mode 100644 contract-ts/02-owner-claims-money/sandbox-test/main.ava.js delete mode 100644 contract-ts/02-owner-claims-money/src/contract.ts rename contract-ts/{02-owner-claims-money => 02-winner-gets-nft}/README.md (100%) rename contract-ts/{02-owner-claims-money => 02-winner-gets-nft}/package.json (100%) rename contract-ts/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/sandbox-test/main.ava.js (100%) rename contract-ts/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/sandbox-test/non_fungible_token.wasm (100%) rename contract-ts/{03-owner-claims-winner-gets-nft => 02-winner-gets-nft}/src/contract.ts (95%) rename contract-ts/{02-owner-claims-money => 02-winner-gets-nft}/tsconfig.json (100%) rename contract-ts/{03-owner-claims-winner-gets-nft => 03-bid-with-fts}/README.md (100%) rename contract-ts/{03-owner-claims-winner-gets-nft => 03-bid-with-fts}/package.json (100%) rename contract-ts/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/sandbox-test/fungible_token.wasm (100%) rename contract-ts/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/sandbox-test/main.ava.js (100%) rename contract-ts/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/sandbox-test/non_fungible_token.wasm (100%) rename contract-ts/{04-ft-owner-claims-winner-gets-nft => 03-bid-with-fts}/src/contract.ts (93%) rename contract-ts/{03-owner-claims-winner-gets-nft => 03-bid-with-fts}/tsconfig.json (100%) delete mode 100644 contract-ts/04-ft-owner-claims-winner-gets-nft/README.md delete mode 100644 contract-ts/04-ft-owner-claims-winner-gets-nft/package.json delete mode 100644 contract-ts/04-ft-owner-claims-winner-gets-nft/tsconfig.json diff --git a/.github/workflows/tests-rs-02.yml b/.github/workflows/tests-rs-02.yml index 552a96a7..95e69cb0 100644 --- a/.github/workflows/tests-rs-02.yml +++ b/.github/workflows/tests-rs-02.yml @@ -10,5 +10,5 @@ jobs: - uses: actions/checkout@v4 - name: Install and test modules run: | - cd ./contract-rs/02-owner-claims-money + cd ./contract-rs/02-winner-gets-nft cargo test diff --git a/.github/workflows/tests-rs-03.yml b/.github/workflows/tests-rs-03.yml index 8edc0f88..a826326c 100644 --- a/.github/workflows/tests-rs-03.yml +++ b/.github/workflows/tests-rs-03.yml @@ -10,5 +10,5 @@ jobs: - uses: actions/checkout@v4 - name: Install and test modules run: | - cd ./contract-rs/03-owner-claims-winner-gets-nft + cd ./contract-rs/03-bid-with-fts cargo test diff --git a/.github/workflows/tests-rs-04.yml b/.github/workflows/tests-rs-04.yml deleted file mode 100644 index fd26812a..00000000 --- a/.github/workflows/tests-rs-04.yml +++ /dev/null @@ -1,14 +0,0 @@ -name: 04 - Tests Contract RS -on: push -jobs: - workflows: - strategy: - matrix: - platform: [ubuntu-latest, macos-latest] - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v4 - - name: Install and test modules - run: | - cd ./contract-rs/04-ft-owner-claims-winner-gets-nft - cargo test diff --git a/.github/workflows/tests-ts-02.yml b/.github/workflows/tests-ts-02.yml index b6f13c59..1576d3a5 100644 --- a/.github/workflows/tests-ts-02.yml +++ b/.github/workflows/tests-ts-02.yml @@ -14,6 +14,6 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install and test modules run: | - cd ./contract-ts/02-owner-claims-money + cd ./contract-ts/02-winner-gets-nft yarn yarn test diff --git a/.github/workflows/tests-ts-03.yml b/.github/workflows/tests-ts-03.yml index 5788e4ec..95213795 100644 --- a/.github/workflows/tests-ts-03.yml +++ b/.github/workflows/tests-ts-03.yml @@ -14,6 +14,6 @@ jobs: node-version: ${{ matrix.node-version }} - name: Install and test modules run: | - cd ./contract-ts/03-owner-claims-winner-gets-nft + cd ./contract-ts/03-bid-with-fts yarn yarn test diff --git a/.github/workflows/tests-ts-04.yml b/.github/workflows/tests-ts-04.yml deleted file mode 100644 index ba288beb..00000000 --- a/.github/workflows/tests-ts-04.yml +++ /dev/null @@ -1,19 +0,0 @@ -name: 04 - Tests Contract TS -on: push -jobs: - workflows: - strategy: - matrix: - platform: [ubuntu-latest, macos-latest] - node-version: [18, 20] - runs-on: ${{ matrix.platform }} - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 - with: - node-version: ${{ matrix.node-version }} - - name: Install and test modules - run: | - cd ./contract-ts/04-ft-owner-claims-winner-gets-nft - yarn - yarn test diff --git a/contract-rs/01-basic-auction/Cargo.toml b/contract-rs/01-basic-auction/Cargo.toml index c456cbef..dcd61c60 100644 --- a/contract-rs/01-basic-auction/Cargo.toml +++ b/contract-rs/01-basic-auction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auction-contract" -description = "Auction Example Part 1" +description = "Auction Example Part 2" version = "0.1.0" edition = "2021" diff --git a/contract-rs/01-basic-auction/src/lib.rs b/contract-rs/01-basic-auction/src/lib.rs index 53f9b889..a9aac4dd 100644 --- a/contract-rs/01-basic-auction/src/lib.rs +++ b/contract-rs/01-basic-auction/src/lib.rs @@ -14,19 +14,23 @@ pub struct Bid { pub struct Contract { highest_bid: Bid, auction_end_time: U64, + auctioneer: AccountId, + claimed: bool, } #[near] impl Contract { #[init] - #[private] // Only callable by the contract's account - pub fn init(end_time: U64) -> Self { + #[private] // only callable by the contract's account + pub fn init(end_time: U64, auctioneer: AccountId) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: NearToken::from_yoctonear(1), }, auction_end_time: end_time, + claimed: false, + auctioneer, } } @@ -58,6 +62,19 @@ impl Contract { Promise::new(last_bidder).transfer(last_bid) } + pub fn claim(&mut self) -> Promise { + require!( + env::block_timestamp() > self.auction_end_time.into(), + "Auction has not ended yet" + ); + + require!(!self.claimed, "Auction has already been claimed"); + self.claimed = true; + + // Transfer tokens to the auctioneer + Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid) + } + pub fn get_highest_bid(&self) -> Bid { self.highest_bid.clone() } @@ -65,25 +82,37 @@ impl Contract { pub fn get_auction_end_time(&self) -> U64 { self.auction_end_time } + + pub fn get_auctioneer(&self) -> AccountId { + self.auctioneer.clone() + } + + pub fn get_claimed(&self) -> bool { + self.claimed + } } -/* - * The rest of this file holds the inline tests for the code above - * Learn more about Rust tests: https://doc.rust-lang.org/book/ch11-01-writing-tests.html - */ #[cfg(test)] mod tests { use super::*; #[test] fn init_contract() { - let contract = Contract::init(U64::from(1000)); + let end_time: U64 = U64::from(1000); + let alice: AccountId = "alice.near".parse().unwrap(); + let contract = Contract::init(end_time.clone(), alice.clone()); let default_bid = contract.get_highest_bid(); assert_eq!(default_bid.bidder, env::current_account_id()); assert_eq!(default_bid.bid, NearToken::from_yoctonear(1)); - let end_time = contract.get_auction_end_time(); - assert_eq!(end_time, U64::from(1000)); + let auction_end_time = contract.get_auction_end_time(); + assert_eq!(auction_end_time, end_time); + + let auctioneer = contract.get_auctioneer(); + assert_eq!(auctioneer, alice); + + let claimed = contract.get_claimed(); + assert_eq!(claimed, false); } } diff --git a/contract-rs/01-basic-auction/tests/test_basics.rs b/contract-rs/01-basic-auction/tests/test_basics.rs index d86ea1d5..a8c0fecf 100644 --- a/contract-rs/01-basic-auction/tests/test_basics.rs +++ b/contract-rs/01-basic-auction/tests/test_basics.rs @@ -1,9 +1,7 @@ use chrono::Utc; -use near_workspaces::types::{NearToken, AccountId}; -use serde_json::json; use near_sdk::near; - -const TEN_NEAR: NearToken = NearToken::from_near(10); +use near_workspaces::types::{AccountId, Gas, NearToken}; +use serde_json::json; #[near(serializers = [json])] #[derive(Clone)] @@ -12,6 +10,8 @@ pub struct Bid { 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?; @@ -21,6 +21,7 @@ async fn test_contract_is_operational() -> Result<(), Box // 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 @@ -32,7 +33,7 @@ async fn test_contract_is_operational() -> Result<(), Box let init = contract .call("init") - .args_json(json!({"end_time": a_minute_from_now.to_string()})) + .args_json(json!({"end_time": a_minute_from_now.to_string(),"auctioneer":auctioneer.id()})) .transact() .await?; @@ -68,7 +69,7 @@ async fn test_contract_is_operational() -> Result<(), Box assert_eq!(highest_bid.bid, NearToken::from_near(2)); assert_eq!(highest_bid.bidder, *bob.id()); - // Check that alice was returned her bid + // Check that Alice was returned her bid let new_alice_balance = alice.view_account().await?.balance; assert!(new_alice_balance == alice_balance.saturating_add(NearToken::from_near(1))); @@ -81,10 +82,45 @@ async fn test_contract_is_operational() -> Result<(), Box assert!(alice_bid.is_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()); + // 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()); + + // Checks the auctioneer has the correct balance + let auctioneer_balance = auctioneer.view_account().await?.balance; + 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()); + // Alice tries to make a bid when the auction is over let alice_bid = alice .call(contract.id(), "bid") diff --git a/contract-rs/02-owner-claims-money/Cargo.toml b/contract-rs/02-owner-claims-money/Cargo.toml deleted file mode 100644 index dcd61c60..00000000 --- a/contract-rs/02-owner-claims-money/Cargo.toml +++ /dev/null @@ -1,27 +0,0 @@ -[package] -name = "auction-contract" -description = "Auction Example Part 2" -version = "0.1.0" -edition = "2021" - -[lib] -crate-type = ["cdylib", "rlib"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -near-sdk = "5.5.0" - -[dev-dependencies] -near-sdk = { version = "5.5.0", features = ["unit-testing"] } -near-workspaces = { version = "0.14.0", features = ["unstable"] } -tokio = { version = "1.12.0", features = ["full"] } -serde_json = "1" -chrono = "0.4.38" - -[profile.release] -codegen-units = 1 -opt-level = "z" -lto = true -debug = false -panic = "abort" -overflow-checks = true \ No newline at end of file diff --git a/contract-rs/02-owner-claims-money/src/lib.rs b/contract-rs/02-owner-claims-money/src/lib.rs deleted file mode 100644 index eb0b47f3..00000000 --- a/contract-rs/02-owner-claims-money/src/lib.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Find all our documentation at https://docs.near.org -use near_sdk::json_types::U64; -use near_sdk::{env, near, require, AccountId, NearToken, PanicOnDefault, Promise}; - -#[near(serializers = [json, borsh])] -#[derive(Clone)] -pub struct Bid { - pub bidder: AccountId, - pub bid: NearToken, -} - -#[near(contract_state, serializers = [json, borsh])] -#[derive(PanicOnDefault)] -pub struct Contract { - highest_bid: Bid, - auction_end_time: U64, - auctioneer: AccountId, - claimed: bool, -} - -#[near] -impl Contract { - #[init] - #[private] // only callable by the contract's account - pub fn init(end_time: U64, auctioneer: AccountId) -> Self { - Self { - highest_bid: Bid { - bidder: env::current_account_id(), - bid: NearToken::from_yoctonear(1), - }, - auction_end_time: end_time, - claimed: false, - auctioneer, - } - } - - #[payable] - pub fn bid(&mut self) -> Promise { - // Assert the auction is still ongoing - require!( - env::block_timestamp() < self.auction_end_time.into(), - "Auction has ended" - ); - - // Current bid - let bid = env::attached_deposit(); - let bidder = env::predecessor_account_id(); - - // Last bid - let Bid { - bidder: last_bidder, - bid: last_bid, - } = self.highest_bid.clone(); - - // Check if the deposit is higher than the current bid - require!(bid > last_bid, "You must place a higher bid"); - - // Update the highest bid - self.highest_bid = Bid { bidder, bid }; - - // Transfer tokens back to the last bidder - Promise::new(last_bidder).transfer(last_bid) - } - - pub fn claim(&mut self) -> Promise { - require!( - env::block_timestamp() > self.auction_end_time.into(), - "Auction has not ended yet" - ); - - require!(!self.claimed, "Auction has already been claimed"); - self.claimed = true; - - // Transfer tokens to the auctioneer - Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid) - } - - pub fn get_highest_bid(&self) -> Bid { - self.highest_bid.clone() - } - - pub fn get_auction_end_time(&self) -> U64 { - self.auction_end_time - } - - pub fn get_auction_info(&self) -> &Contract { - self - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn init_contract() { - let end_time: U64 = U64::from(1000); - let alice: AccountId = "alice.near".parse().unwrap(); - let contract = Contract::init(end_time.clone(), alice.clone()); - - let default_bid = contract.get_highest_bid(); - assert_eq!(default_bid.bidder, env::current_account_id()); - assert_eq!(default_bid.bid, NearToken::from_yoctonear(1)); - - let auction_info = contract.get_auction_info(); - assert_eq!(auction_info.auction_end_time, end_time); - assert_eq!(auction_info.auctioneer, alice); - assert_eq!(auction_info.claimed, false); - } -} diff --git a/contract-rs/02-owner-claims-money/tests/test_basics.rs b/contract-rs/02-owner-claims-money/tests/test_basics.rs deleted file mode 100644 index a503b20f..00000000 --- a/contract-rs/02-owner-claims-money/tests/test_basics.rs +++ /dev/null @@ -1,148 +0,0 @@ -use chrono::Utc; -use serde_json::json; -use near_workspaces::types::{NearToken, AccountId, Gas}; -use near_sdk::near; - -#[near(serializers = [json])] -#[derive(Clone)] -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?; - - let root = 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?; - - // 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 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()); - - // Alice makes first bid - let alice_bid = alice - .call(contract.id(), "bid") - .deposit(NearToken::from_near(1)) - .transact() - .await?; - - assert!(alice_bid.is_success()); - - let highest_bid_json = contract.view("get_highest_bid").await?; - let highest_bid: Bid = highest_bid_json.json::()?; - assert_eq!(highest_bid.bid, NearToken::from_near(1)); - assert_eq!(highest_bid.bidder, *alice.id()); - - let alice_balance = alice.view_account().await?.balance; - - // Bob makes a higher bid - let bob_bid = bob - .call(contract.id(), "bid") - .deposit(NearToken::from_near(2)) - .transact() - .await?; - - assert!(bob_bid.is_success()); - - let highest_bid_json = contract.view("get_highest_bid").await?; - let highest_bid: Bid = highest_bid_json.json::()?; - assert_eq!(highest_bid.bid, NearToken::from_near(2)); - assert_eq!(highest_bid.bidder, *bob.id()); - - // Check that Alice was returned her bid - let new_alice_balance = alice.view_account().await?.balance; - 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") - .deposit(NearToken::from_near(1)) - .transact() - .await?; - - assert!(alice_bid.is_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()); - - // 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()); - - // Checks the auctioneer has the correct balance - let auctioneer_balance = auctioneer.view_account().await?.balance; - 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()); - - // 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()); - - Ok(()) -} - -async fn create_subaccount( - root: &near_workspaces::Account, - name: &str, -) -> Result> { - let subaccount = root - .create_subaccount(name) - .initial_balance(TEN_NEAR) - .transact() - .await? - .unwrap(); - - Ok(subaccount) -} diff --git a/contract-rs/03-owner-claims-winner-gets-nft/Cargo.toml b/contract-rs/02-winner-gets-nft/Cargo.toml similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/Cargo.toml rename to contract-rs/02-winner-gets-nft/Cargo.toml diff --git a/contract-rs/02-owner-claims-money/README.md b/contract-rs/02-winner-gets-nft/README.md similarity index 100% rename from contract-rs/02-owner-claims-money/README.md rename to contract-rs/02-winner-gets-nft/README.md diff --git a/contract-rs/02-owner-claims-money/rust-toolchain.toml b/contract-rs/02-winner-gets-nft/rust-toolchain.toml similarity index 100% rename from contract-rs/02-owner-claims-money/rust-toolchain.toml rename to contract-rs/02-winner-gets-nft/rust-toolchain.toml diff --git a/contract-rs/03-owner-claims-winner-gets-nft/src/ext.rs b/contract-rs/02-winner-gets-nft/src/ext.rs similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/src/ext.rs rename to contract-rs/02-winner-gets-nft/src/ext.rs diff --git a/contract-rs/03-owner-claims-winner-gets-nft/src/lib.rs b/contract-rs/02-winner-gets-nft/src/lib.rs similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/src/lib.rs rename to contract-rs/02-winner-gets-nft/src/lib.rs diff --git a/contract-rs/03-owner-claims-winner-gets-nft/tests/non_fungible_token.wasm b/contract-rs/02-winner-gets-nft/tests/non_fungible_token.wasm similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/tests/non_fungible_token.wasm rename to contract-rs/02-winner-gets-nft/tests/non_fungible_token.wasm diff --git a/contract-rs/03-owner-claims-winner-gets-nft/tests/test_basics.rs b/contract-rs/02-winner-gets-nft/tests/test_basics.rs similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/tests/test_basics.rs rename to contract-rs/02-winner-gets-nft/tests/test_basics.rs diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/Cargo.toml b/contract-rs/03-bid-with-fts/Cargo.toml similarity index 100% rename from contract-rs/04-ft-owner-claims-winner-gets-nft/Cargo.toml rename to contract-rs/03-bid-with-fts/Cargo.toml diff --git a/contract-rs/03-owner-claims-winner-gets-nft/README.md b/contract-rs/03-bid-with-fts/README.md similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/README.md rename to contract-rs/03-bid-with-fts/README.md diff --git a/contract-rs/03-owner-claims-winner-gets-nft/rust-toolchain.toml b/contract-rs/03-bid-with-fts/rust-toolchain.toml similarity index 100% rename from contract-rs/03-owner-claims-winner-gets-nft/rust-toolchain.toml rename to contract-rs/03-bid-with-fts/rust-toolchain.toml diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/src/ext.rs b/contract-rs/03-bid-with-fts/src/ext.rs similarity index 100% rename from contract-rs/04-ft-owner-claims-winner-gets-nft/src/ext.rs rename to contract-rs/03-bid-with-fts/src/ext.rs diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/src/lib.rs b/contract-rs/03-bid-with-fts/src/lib.rs similarity index 100% rename from contract-rs/04-ft-owner-claims-winner-gets-nft/src/lib.rs rename to contract-rs/03-bid-with-fts/src/lib.rs diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/tests/fungible_token.wasm b/contract-rs/03-bid-with-fts/tests/fungible_token.wasm similarity index 100% rename from contract-rs/04-ft-owner-claims-winner-gets-nft/tests/fungible_token.wasm rename to contract-rs/03-bid-with-fts/tests/fungible_token.wasm diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/tests/non_fungible_token.wasm b/contract-rs/03-bid-with-fts/tests/non_fungible_token.wasm similarity index 100% rename from contract-rs/04-ft-owner-claims-winner-gets-nft/tests/non_fungible_token.wasm rename to contract-rs/03-bid-with-fts/tests/non_fungible_token.wasm diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/tests/test_basics.rs b/contract-rs/03-bid-with-fts/tests/test_basics.rs similarity index 100% rename from contract-rs/04-ft-owner-claims-winner-gets-nft/tests/test_basics.rs rename to contract-rs/03-bid-with-fts/tests/test_basics.rs diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/README.md b/contract-rs/04-ft-owner-claims-winner-gets-nft/README.md deleted file mode 100644 index ebc0f142..00000000 --- a/contract-rs/04-ft-owner-claims-winner-gets-nft/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# contract-rs - -cargo-near-new-project-description - -## How to Build Locally? - -Install [`cargo-near`](https://github.com/near/cargo-near) and run: - -```bash -cargo near build -``` - -## How to Test Locally? - -```bash -cargo test -``` - -## How to Deploy? - -To deploy manually, install [`cargo-near`](https://github.com/near/cargo-near) and run: - -```bash -# Create a new account -cargo near create-dev-account - -# Deploy the contract on it -cargo near deploy -``` -## Useful Links - -- [cargo-near](https://github.com/near/cargo-near) - NEAR smart contract development toolkit for Rust -- [near CLI](https://near.cli.rs) - Iteract with NEAR blockchain from command line -- [NEAR Rust SDK Documentation](https://docs.near.org/sdk/rust/introduction) -- [NEAR Documentation](https://docs.near.org) -- [NEAR StackOverflow](https://stackoverflow.com/questions/tagged/nearprotocol) -- [NEAR Discord](https://near.chat) -- [NEAR Telegram Developers Community Group](https://t.me/neardev) -- NEAR DevHub: [Telegram](https://t.me/neardevhub), [Twitter](https://twitter.com/neardevhub) diff --git a/contract-rs/04-ft-owner-claims-winner-gets-nft/rust-toolchain.toml b/contract-rs/04-ft-owner-claims-winner-gets-nft/rust-toolchain.toml deleted file mode 100644 index a82ade34..00000000 --- a/contract-rs/04-ft-owner-claims-winner-gets-nft/rust-toolchain.toml +++ /dev/null @@ -1,4 +0,0 @@ -[toolchain] -channel = "stable" -components = ["rustfmt"] -targets = ["wasm32-unknown-unknown"] diff --git a/contract-ts/01-basic-auction/package.json b/contract-ts/01-basic-auction/package.json index 45eb68fc..7569b624 100644 --- a/contract-ts/01-basic-auction/package.json +++ b/contract-ts/01-basic-auction/package.json @@ -8,7 +8,7 @@ "test": "$npm_execpath run build && ava -- ./build/auction.wasm" }, "dependencies": { - "near-sdk-js": "2.0.0" + "near-sdk-js": "1.0.0" }, "devDependencies": { "ava": "^6.1.3", diff --git a/contract-ts/01-basic-auction/sandbox-test/main.ava.js b/contract-ts/01-basic-auction/sandbox-test/main.ava.js index b537511e..a3e455a1 100644 --- a/contract-ts/01-basic-auction/sandbox-test/main.ava.js +++ b/contract-ts/01-basic-auction/sandbox-test/main.ava.js @@ -16,6 +16,7 @@ test.beforeEach(async (t) => { const alice = await root.createSubAccount("alice", { initialBalance: NEAR.parse("10 N").toString() }); const bob = await root.createSubAccount("bob", { initialBalance: NEAR.parse("10 N").toString() }); + const auctioneer = await root.createSubAccount("auctioneer", { initialBalance: NEAR.parse("10 N").toString() }); const contract = await root.createSubAccount("contract", { initialBalance: NEAR.parse("10 N").toString() }); // Deploy contract (input from package.json) @@ -24,11 +25,12 @@ test.beforeEach(async (t) => { // Initialize contract, finishes in 1 minute await contract.call(contract, "init", { end_time: String((Date.now() + 60000) * 10 ** 6), + auctioneer: auctioneer.accountId, }); // Save state for test runs, it is unique for each test t.context.worker = worker; - t.context.accounts = { alice, bob, contract }; + t.context.accounts = { alice, bob, contract, auctioneer }; }); test.afterEach.always(async (t) => { @@ -39,14 +41,13 @@ test.afterEach.always(async (t) => { }); test("Test full contract", async (t) => { - const { alice, bob, contract } = t.context.accounts; + const { alice, bob, auctioneer, contract } = t.context.accounts; // Alice makes first bid await alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() }); let highest_bid = await contract.view("get_highest_bid", {}); t.is(highest_bid.bidder, alice.accountId); t.is(highest_bid.bid, NEAR.parse("1 N").toString()); - const aliceBalance = await alice.balance(); // Bob makes a higher bid @@ -59,12 +60,29 @@ test("Test full contract", async (t) => { const aliceNewBalance = await alice.balance(); t.deepEqual(aliceNewBalance.available, aliceBalance.available.add(NEAR.parse("1 N"))); - // Alice tries to make a bid with less NEAR than the previous - await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() })) + // Alice tires to make a bid with less NEAR than the previous + await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() })); + + // Auctioneer claims auction but did not finish + await t.throwsAsync(auctioneer.call(contract, "claim", {}, { gas: "300000000000000" })); // Fast forward 200 blocks await t.context.worker.provider.fastForward(200) + const auctioneerBalance = await auctioneer.balance(); + const available = parseFloat(auctioneerBalance.available.toHuman()); + + // Auctioneer claims the auction + await auctioneer.call(contract, "claim", {}, { gas: "300000000000000" }); + + // Checks that the auctioneer has the correct balance + const contractNewBalance = await auctioneer.balance(); + const new_available = parseFloat(contractNewBalance.available.toHuman()); + t.is(new_available.toFixed(2), (available + 2).toFixed(2)); + + // Auctioneer tries to claim the auction again + await t.throwsAsync(auctioneer.call(contract, "claim", {}, { gas: "300000000000000" })) + // Alice tries to make a bid when the auction is over - await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("3 N").toString() })) + await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() })); }); \ No newline at end of file diff --git a/contract-ts/01-basic-auction/src/contract.ts b/contract-ts/01-basic-auction/src/contract.ts index 484fa957..15805837 100644 --- a/contract-ts/01-basic-auction/src/contract.ts +++ b/contract-ts/01-basic-auction/src/contract.ts @@ -10,11 +10,14 @@ class Bid { class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(0) }; auction_end_time: bigint = BigInt(0); + auctioneer: AccountId = ""; + claimed: boolean = false; @initialize({ privateFunction: true }) - init({ end_time}: { end_time: bigint}) { + init({ end_time, auctioneer}: { end_time: bigint, auctioneer: AccountId}) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; + this.auctioneer = auctioneer; } @call({ payableFunction: true }) @@ -33,7 +36,7 @@ class AuctionContract { assert(bid > lastBid, "You must place a higher bid"); // Update the highest bid - this.highest_bid = { bidder, bid }; + this.highest_bid = { bidder, bid }; // Save the new bid // Transfer tokens back to the last bidder return NearPromise.new(lastBidder).transfer(lastBid); @@ -48,4 +51,22 @@ class AuctionContract { get_auction_end_time(): BigInt { return this.auction_end_time; } + + @view({}) + get_auctioneer(): AccountId { + return this.auctioneer; + } + + @view({}) + get_claimed(): boolean { + return this.claimed; + } + + @call({}) + claim() { + assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); + assert(!this.claimed, "Auction has been claimed"); + this.claimed = true; + return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) + } } \ No newline at end of file diff --git a/contract-ts/02-owner-claims-money/sandbox-test/main.ava.js b/contract-ts/02-owner-claims-money/sandbox-test/main.ava.js deleted file mode 100644 index a3e455a1..00000000 --- a/contract-ts/02-owner-claims-money/sandbox-test/main.ava.js +++ /dev/null @@ -1,88 +0,0 @@ -import anyTest from 'ava'; -import { NEAR, Worker } from 'near-workspaces'; -import { setDefaultResultOrder } from 'dns'; setDefaultResultOrder('ipv4first'); // temp fix for node >v17 - -/** - * @typedef {import('near-workspaces').NearAccount} NearAccount - * @type {import('ava').TestFn<{worker: Worker, accounts: Record}>} - */ -const test = anyTest; -test.beforeEach(async (t) => { - // Init the worker and start a Sandbox server - const worker = t.context.worker = await Worker.init(); - - // Create accounts - const root = worker.rootAccount; - - const alice = await root.createSubAccount("alice", { initialBalance: NEAR.parse("10 N").toString() }); - const bob = await root.createSubAccount("bob", { initialBalance: NEAR.parse("10 N").toString() }); - const auctioneer = await root.createSubAccount("auctioneer", { initialBalance: NEAR.parse("10 N").toString() }); - const contract = await root.createSubAccount("contract", { initialBalance: NEAR.parse("10 N").toString() }); - - // Deploy contract (input from package.json) - await contract.deploy(process.argv[2]); - - // Initialize contract, finishes in 1 minute - await contract.call(contract, "init", { - end_time: String((Date.now() + 60000) * 10 ** 6), - auctioneer: auctioneer.accountId, - }); - - // Save state for test runs, it is unique for each test - t.context.worker = worker; - t.context.accounts = { alice, bob, contract, auctioneer }; -}); - -test.afterEach.always(async (t) => { - // Stop Sandbox server - await t.context.worker.tearDown().catch((error) => { - console.log('Failed to stop the Sandbox:', error); - }); -}); - -test("Test full contract", async (t) => { - const { alice, bob, auctioneer, contract } = t.context.accounts; - - // Alice makes first bid - await alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() }); - let highest_bid = await contract.view("get_highest_bid", {}); - t.is(highest_bid.bidder, alice.accountId); - t.is(highest_bid.bid, NEAR.parse("1 N").toString()); - const aliceBalance = await alice.balance(); - - // Bob makes a higher bid - await bob.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("2 N").toString() }); - highest_bid = await contract.view("get_highest_bid", {}); - t.is(highest_bid.bidder, bob.accountId); - t.is(highest_bid.bid, NEAR.parse("2 N").toString()); - - // Check that alice was returned her bid - const aliceNewBalance = await alice.balance(); - t.deepEqual(aliceNewBalance.available, aliceBalance.available.add(NEAR.parse("1 N"))); - - // Alice tires to make a bid with less NEAR than the previous - await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() })); - - // Auctioneer claims auction but did not finish - await t.throwsAsync(auctioneer.call(contract, "claim", {}, { gas: "300000000000000" })); - - // Fast forward 200 blocks - await t.context.worker.provider.fastForward(200) - - const auctioneerBalance = await auctioneer.balance(); - const available = parseFloat(auctioneerBalance.available.toHuman()); - - // Auctioneer claims the auction - await auctioneer.call(contract, "claim", {}, { gas: "300000000000000" }); - - // Checks that the auctioneer has the correct balance - const contractNewBalance = await auctioneer.balance(); - const new_available = parseFloat(contractNewBalance.available.toHuman()); - t.is(new_available.toFixed(2), (available + 2).toFixed(2)); - - // Auctioneer tries to claim the auction again - await t.throwsAsync(auctioneer.call(contract, "claim", {}, { gas: "300000000000000" })) - - // Alice tries to make a bid when the auction is over - await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() })); -}); \ No newline at end of file diff --git a/contract-ts/02-owner-claims-money/src/contract.ts b/contract-ts/02-owner-claims-money/src/contract.ts deleted file mode 100644 index 17fb7314..00000000 --- a/contract-ts/02-owner-claims-money/src/contract.ts +++ /dev/null @@ -1,62 +0,0 @@ -// Find all our documentation at https://docs.near.org -import { NearBindgen, near, call, view, AccountId, NearPromise, initialize, assert } from "near-sdk-js"; - -class Bid { - bidder: AccountId; - bid: bigint; -} - -@NearBindgen({ requireInit: true }) -class AuctionContract { - highest_bid: Bid = { bidder: '', bid: BigInt(0) }; - auction_end_time: bigint = BigInt(0); - auctioneer: string = ""; - claimed: boolean = false; - - @initialize({ privateFunction: true }) - init({ end_time, auctioneer}: { end_time: bigint, auctioneer: string}) { - this.auction_end_time = end_time; - this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; - this.auctioneer = auctioneer; - } - - @call({ payableFunction: true }) - bid(): NearPromise { - // Assert the auction is still ongoing - assert(this.auction_end_time > near.blockTimestamp(), "Auction has ended"); - - // Current bid - const bid = near.attachedDeposit(); - const bidder = near.predecessorAccountId(); - - // Last bid - const { bidder: lastBidder, bid: lastBid } = this.highest_bid; - - // Check if the deposit is higher than the current bid - assert(bid > lastBid, "You must place a higher bid"); - - // Update the highest bid - this.highest_bid = { bidder, bid }; // Save the new bid - - // Transfer tokens back to the last bidder - return NearPromise.new(lastBidder).transfer(lastBid); - } - - @view({}) - get_highest_bid(): Bid { - return this.highest_bid; - } - - @view({}) - get_auction_end_time(): BigInt { - return this.auction_end_time; - } - - @call({}) - claim() { - assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); - assert(!this.claimed, "Auction has been claimed"); - this.claimed = true; - return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) - } -} \ No newline at end of file diff --git a/contract-ts/02-owner-claims-money/README.md b/contract-ts/02-winner-gets-nft/README.md similarity index 100% rename from contract-ts/02-owner-claims-money/README.md rename to contract-ts/02-winner-gets-nft/README.md diff --git a/contract-ts/02-owner-claims-money/package.json b/contract-ts/02-winner-gets-nft/package.json similarity index 100% rename from contract-ts/02-owner-claims-money/package.json rename to contract-ts/02-winner-gets-nft/package.json diff --git a/contract-ts/03-owner-claims-winner-gets-nft/sandbox-test/main.ava.js b/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js similarity index 100% rename from contract-ts/03-owner-claims-winner-gets-nft/sandbox-test/main.ava.js rename to contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js diff --git a/contract-ts/03-owner-claims-winner-gets-nft/sandbox-test/non_fungible_token.wasm b/contract-ts/02-winner-gets-nft/sandbox-test/non_fungible_token.wasm similarity index 100% rename from contract-ts/03-owner-claims-winner-gets-nft/sandbox-test/non_fungible_token.wasm rename to contract-ts/02-winner-gets-nft/sandbox-test/non_fungible_token.wasm diff --git a/contract-ts/03-owner-claims-winner-gets-nft/src/contract.ts b/contract-ts/02-winner-gets-nft/src/contract.ts similarity index 95% rename from contract-ts/03-owner-claims-winner-gets-nft/src/contract.ts rename to contract-ts/02-winner-gets-nft/src/contract.ts index ceb0c794..fbd11c94 100644 --- a/contract-ts/03-owner-claims-winner-gets-nft/src/contract.ts +++ b/contract-ts/02-winner-gets-nft/src/contract.ts @@ -13,13 +13,13 @@ const NO_DEPOSIT = BigInt(0); class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(0) }; auction_end_time: bigint = BigInt(0); - auctioneer: string = ""; + auctioneer: AccountId = ""; claimed: boolean = false; nft_contract: AccountId = ""; token_id: string = ""; @initialize({ privateFunction: true }) - init({ end_time, auctioneer, nft_contract, token_id }: { end_time: bigint, auctioneer: string, nft_contract: AccountId, token_id: string }) { + init({ end_time, auctioneer, nft_contract, token_id }: { end_time: bigint, auctioneer: AccountId, nft_contract: AccountId, token_id: string }) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; this.auctioneer = auctioneer; diff --git a/contract-ts/02-owner-claims-money/tsconfig.json b/contract-ts/02-winner-gets-nft/tsconfig.json similarity index 100% rename from contract-ts/02-owner-claims-money/tsconfig.json rename to contract-ts/02-winner-gets-nft/tsconfig.json diff --git a/contract-ts/03-owner-claims-winner-gets-nft/README.md b/contract-ts/03-bid-with-fts/README.md similarity index 100% rename from contract-ts/03-owner-claims-winner-gets-nft/README.md rename to contract-ts/03-bid-with-fts/README.md diff --git a/contract-ts/03-owner-claims-winner-gets-nft/package.json b/contract-ts/03-bid-with-fts/package.json similarity index 100% rename from contract-ts/03-owner-claims-winner-gets-nft/package.json rename to contract-ts/03-bid-with-fts/package.json diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/sandbox-test/fungible_token.wasm b/contract-ts/03-bid-with-fts/sandbox-test/fungible_token.wasm similarity index 100% rename from contract-ts/04-ft-owner-claims-winner-gets-nft/sandbox-test/fungible_token.wasm rename to contract-ts/03-bid-with-fts/sandbox-test/fungible_token.wasm diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/sandbox-test/main.ava.js b/contract-ts/03-bid-with-fts/sandbox-test/main.ava.js similarity index 100% rename from contract-ts/04-ft-owner-claims-winner-gets-nft/sandbox-test/main.ava.js rename to contract-ts/03-bid-with-fts/sandbox-test/main.ava.js diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/sandbox-test/non_fungible_token.wasm b/contract-ts/03-bid-with-fts/sandbox-test/non_fungible_token.wasm similarity index 100% rename from contract-ts/04-ft-owner-claims-winner-gets-nft/sandbox-test/non_fungible_token.wasm rename to contract-ts/03-bid-with-fts/sandbox-test/non_fungible_token.wasm diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/src/contract.ts b/contract-ts/03-bid-with-fts/src/contract.ts similarity index 93% rename from contract-ts/04-ft-owner-claims-winner-gets-nft/src/contract.ts rename to contract-ts/03-bid-with-fts/src/contract.ts index b3accfe1..9a965a5a 100644 --- a/contract-ts/04-ft-owner-claims-winner-gets-nft/src/contract.ts +++ b/contract-ts/03-bid-with-fts/src/contract.ts @@ -13,14 +13,14 @@ const NO_DEPOSIT = BigInt(0); class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(1) }; auction_end_time: bigint = BigInt(0); - auctioneer: string = ""; + auctioneer: AccountId = ""; claimed: boolean = false; ft_contract: AccountId = ""; nft_contract: AccountId = ""; token_id: string = ""; @initialize({ privateFunction: true }) - init({ end_time, auctioneer, ft_contract, nft_contract, token_id, starting_price }: { end_time: bigint, auctioneer: string, ft_contract: AccountId, nft_contract: AccountId, token_id: string, starting_price: bigint }) { + init({ end_time, auctioneer, ft_contract, nft_contract, token_id, starting_price }: { end_time: bigint, auctioneer: AccountId, ft_contract: AccountId, nft_contract: AccountId, token_id: string, starting_price: bigint }) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: starting_price }; this.auctioneer = auctioneer; diff --git a/contract-ts/03-owner-claims-winner-gets-nft/tsconfig.json b/contract-ts/03-bid-with-fts/tsconfig.json similarity index 100% rename from contract-ts/03-owner-claims-winner-gets-nft/tsconfig.json rename to contract-ts/03-bid-with-fts/tsconfig.json diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/README.md b/contract-ts/04-ft-owner-claims-winner-gets-nft/README.md deleted file mode 100644 index 43242c2a..00000000 --- a/contract-ts/04-ft-owner-claims-winner-gets-nft/README.md +++ /dev/null @@ -1,83 +0,0 @@ -# Hello NEAR Contract - -The smart contract exposes two methods to enable storing and retrieving a greeting in the NEAR network. - -```ts -@NearBindgen({}) -class HelloNear { - greeting: string = "Hello"; - - @view // This method is read-only and can be called for free - get_greeting(): string { - return this.greeting; - } - - @call // This method changes the state, for which it cost gas - set_greeting({ greeting }: { greeting: string }): void { - // Record a log permanently to the blockchain! - near.log(`Saving greeting ${greeting}`); - this.greeting = greeting; - } -} -``` - -
- -# Quickstart - -1. Make sure you have installed [node.js](https://nodejs.org/en/download/package-manager/) >= 16. -2. Install the [`NEAR CLI`](https://github.com/near/near-cli#setup) - -
- -## 1. Build and Test the Contract -You can automatically compile and test the contract by running: - -```bash -npm run build -``` - -
- -## 2. Create an Account and Deploy the Contract -You can create a new account and deploy the contract by running: - -```bash -near create-account --useFaucet -near deploy build/release/hello_near.wasm -``` - -
- - -## 3. Retrieve the Greeting - -`get_greeting` is a read-only method (aka `view` method). - -`View` methods can be called for **free** by anyone, even people **without a NEAR account**! - -```bash -# Use near-cli to get the greeting -near view get_greeting -``` - -
- -## 4. Store a New Greeting -`set_greeting` changes the contract's state, for which it is a `call` method. - -`Call` methods can only be invoked using a NEAR account, since the account needs to pay GAS for the transaction. - -```bash -# Use near-cli to set a new greeting -near call set_greeting '{"greeting":"howdy"}' --accountId -``` - -**Tip:** If you would like to call `set_greeting` using another account, first login into NEAR using: - -```bash -# Use near-cli to login your NEAR account -near login -``` - -and then use the logged account to sign the transaction: `--accountId `. \ No newline at end of file diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/package.json b/contract-ts/04-ft-owner-claims-winner-gets-nft/package.json deleted file mode 100644 index 7569b624..00000000 --- a/contract-ts/04-ft-owner-claims-winner-gets-nft/package.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "name": "auction", - "version": "1.0.0", - "license": "(MIT AND Apache-2.0)", - "type": "module", - "scripts": { - "build": "near-sdk-js build src/contract.ts build/auction.wasm", - "test": "$npm_execpath run build && ava -- ./build/auction.wasm" - }, - "dependencies": { - "near-sdk-js": "1.0.0" - }, - "devDependencies": { - "ava": "^6.1.3", - "near-workspaces": "^3.5.0", - "typescript": "^5.4.5" - }, - "ava": { - "timeout": "50000", - "files": ["sandbox-test/*.ava.js"] - } -} diff --git a/contract-ts/04-ft-owner-claims-winner-gets-nft/tsconfig.json b/contract-ts/04-ft-owner-claims-winner-gets-nft/tsconfig.json deleted file mode 100644 index c3d38e60..00000000 --- a/contract-ts/04-ft-owner-claims-winner-gets-nft/tsconfig.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "compilerOptions": { - "experimentalDecorators": true, - "target": "ES5", - "noEmit": true, - "noImplicitAny": false, - }, - "files": [ - "src/contract.ts" - ], - "exclude": [ - "node_modules" - ], -} \ No newline at end of file From dc077a83711f2b29a0d5d3a988c199c060945d23 Mon Sep 17 00:00:00 2001 From: PiVortex Date: Fri, 4 Oct 2024 15:32:16 +0700 Subject: [PATCH 02/11] update to near-sdk-js v2 --- contract-rs/01-basic-auction/Cargo.toml | 2 +- contract-rs/02-winner-gets-nft/Cargo.toml | 2 +- contract-rs/03-bid-with-fts/Cargo.toml | 2 +- contract-ts/01-basic-auction/package.json | 8 ++++---- contract-ts/01-basic-auction/sandbox-test/main.ava.js | 2 +- contract-ts/02-winner-gets-nft/package.json | 8 ++++---- contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js | 4 +++- contract-ts/03-bid-with-fts/package.json | 8 ++++---- 8 files changed, 19 insertions(+), 17 deletions(-) diff --git a/contract-rs/01-basic-auction/Cargo.toml b/contract-rs/01-basic-auction/Cargo.toml index dcd61c60..c456cbef 100644 --- a/contract-rs/01-basic-auction/Cargo.toml +++ b/contract-rs/01-basic-auction/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auction-contract" -description = "Auction Example Part 2" +description = "Auction Example Part 1" version = "0.1.0" edition = "2021" diff --git a/contract-rs/02-winner-gets-nft/Cargo.toml b/contract-rs/02-winner-gets-nft/Cargo.toml index b3338c0d..999b8076 100644 --- a/contract-rs/02-winner-gets-nft/Cargo.toml +++ b/contract-rs/02-winner-gets-nft/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auction-contract" -description = "Auction Example Part 3" +description = "Auction Example Part 2" version = "0.1.0" edition = "2021" diff --git a/contract-rs/03-bid-with-fts/Cargo.toml b/contract-rs/03-bid-with-fts/Cargo.toml index b8dd9566..211cf7a6 100644 --- a/contract-rs/03-bid-with-fts/Cargo.toml +++ b/contract-rs/03-bid-with-fts/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "auction-contract" -description = "Auction Example Part 4" +description = "Auction Example Part 3" version = "0.1.0" edition = "2021" diff --git a/contract-ts/01-basic-auction/package.json b/contract-ts/01-basic-auction/package.json index 7569b624..112b7683 100644 --- a/contract-ts/01-basic-auction/package.json +++ b/contract-ts/01-basic-auction/package.json @@ -1,14 +1,14 @@ { - "name": "auction", + "name": "auction-contract", "version": "1.0.0", "license": "(MIT AND Apache-2.0)", "type": "module", "scripts": { - "build": "near-sdk-js build src/contract.ts build/auction.wasm", - "test": "$npm_execpath run build && ava -- ./build/auction.wasm" + "build": "near-sdk-js build src/contract.ts build/auction-contract.wasm", + "test": "$npm_execpath run build && ava -- ./build/auction-contract.wasm" }, "dependencies": { - "near-sdk-js": "1.0.0" + "near-sdk-js": "2.0.0" }, "devDependencies": { "ava": "^6.1.3", diff --git a/contract-ts/01-basic-auction/sandbox-test/main.ava.js b/contract-ts/01-basic-auction/sandbox-test/main.ava.js index a3e455a1..f6012a0c 100644 --- a/contract-ts/01-basic-auction/sandbox-test/main.ava.js +++ b/contract-ts/01-basic-auction/sandbox-test/main.ava.js @@ -78,7 +78,7 @@ test("Test full contract", async (t) => { // Checks that the auctioneer has the correct balance const contractNewBalance = await auctioneer.balance(); const new_available = parseFloat(contractNewBalance.available.toHuman()); - t.is(new_available.toFixed(2), (available + 2).toFixed(2)); + t.is(new_available.toFixed(1), (available + 2).toFixed(1)); // Auctioneer tries to claim the auction again await t.throwsAsync(auctioneer.call(contract, "claim", {}, { gas: "300000000000000" })) diff --git a/contract-ts/02-winner-gets-nft/package.json b/contract-ts/02-winner-gets-nft/package.json index 7569b624..112b7683 100644 --- a/contract-ts/02-winner-gets-nft/package.json +++ b/contract-ts/02-winner-gets-nft/package.json @@ -1,14 +1,14 @@ { - "name": "auction", + "name": "auction-contract", "version": "1.0.0", "license": "(MIT AND Apache-2.0)", "type": "module", "scripts": { - "build": "near-sdk-js build src/contract.ts build/auction.wasm", - "test": "$npm_execpath run build && ava -- ./build/auction.wasm" + "build": "near-sdk-js build src/contract.ts build/auction-contract.wasm", + "test": "$npm_execpath run build && ava -- ./build/auction-contract.wasm" }, "dependencies": { - "near-sdk-js": "1.0.0" + "near-sdk-js": "2.0.0" }, "devDependencies": { "ava": "^6.1.3", diff --git a/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js b/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js index 6ae75ae0..6ccb346d 100644 --- a/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js +++ b/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js @@ -98,8 +98,10 @@ test("Test full contract", async (t) => { // Checks that the auctioneer has the correct balance const contractNewBalance = await auctioneer.balance(); + console.log("contractNewBalance", contractNewBalance); const new_available = parseFloat(contractNewBalance.available.toHuman()); - t.is(new_available.toFixed(2), (available + 2).toFixed(2)); + console.log("new_available", new_available); + t.is(new_available.toFixed(1), (available + 2).toFixed(1)); // Check highest bidder received the NFT const response = await nft_contract.call(nft_contract, "nft_token",{"token_id": "1"},{ gas: "300000000000000" }); diff --git a/contract-ts/03-bid-with-fts/package.json b/contract-ts/03-bid-with-fts/package.json index 7569b624..112b7683 100644 --- a/contract-ts/03-bid-with-fts/package.json +++ b/contract-ts/03-bid-with-fts/package.json @@ -1,14 +1,14 @@ { - "name": "auction", + "name": "auction-contract", "version": "1.0.0", "license": "(MIT AND Apache-2.0)", "type": "module", "scripts": { - "build": "near-sdk-js build src/contract.ts build/auction.wasm", - "test": "$npm_execpath run build && ava -- ./build/auction.wasm" + "build": "near-sdk-js build src/contract.ts build/auction-contract.wasm", + "test": "$npm_execpath run build && ava -- ./build/auction-contract.wasm" }, "dependencies": { - "near-sdk-js": "1.0.0" + "near-sdk-js": "2.0.0" }, "devDependencies": { "ava": "^6.1.3", From f30d431a83b66d028fc4f9b3a6815ddf9d325d16 Mon Sep 17 00:00:00 2001 From: PiVortex Date: Fri, 4 Oct 2024 15:36:21 +0700 Subject: [PATCH 03/11] clone frontend, reorg repo --- .../01-frontend}/.eslintrc.json | 0 {frontend => frontends/01-frontend}/README.md | 0 .../01-frontend}/jsconfig.json | 0 .../01-frontend}/next.config.js | 0 .../01-frontend}/package.json | 2 +- .../01-frontend}/public/favicon.ico | Bin .../01-frontend}/public/near-logo.svg | 0 .../01-frontend}/public/near.svg | 0 .../01-frontend}/public/next.svg | 0 .../01-frontend}/public/vercel.svg | 0 .../src/components/AuctionItem.jsx | 0 .../src/components/AuctionItem.module.css | 0 .../01-frontend}/src/components/Bid.jsx | 0 .../src/components/Bid.module.css | 0 .../01-frontend}/src/components/LastBid.jsx | 0 .../src/components/LastBid.module.css | 0 .../src/components/Navigation.jsx | 0 .../components/Skeletons/Skeleton.module.css | 0 .../Skeletons/SkeletonAuctionItem.jsx | 0 .../src/components/Skeletons/SkeletonBid.jsx | 0 .../components/Skeletons/SkeletonTimer.jsx | 0 .../01-frontend}/src/components/Timer.jsx | 0 .../src/components/Timer.module.css | 0 .../01-frontend}/src/config.js | 0 .../01-frontend}/src/context.js | 0 .../01-frontend}/src/pages/_app.js | 0 .../src/pages/api/getBidHistory.js | 0 .../01-frontend}/src/pages/index.js | 0 .../01-frontend}/src/styles/app.module.css | 0 .../01-frontend}/src/styles/globals.css | 0 .../01-frontend}/src/wallets/near.js | 0 frontends/03-frontend/.eslintrc.json | 3 + frontends/03-frontend/README.md | 45 ++++++ frontends/03-frontend/jsconfig.json | 7 + frontends/03-frontend/next.config.js | 6 + frontends/03-frontend/package.json | 31 ++++ frontends/03-frontend/public/favicon.ico | Bin 0 -> 908 bytes frontends/03-frontend/public/near-logo.svg | 43 +++++ frontends/03-frontend/public/near.svg | 1 + frontends/03-frontend/public/next.svg | 1 + frontends/03-frontend/public/vercel.svg | 1 + .../src/components/AuctionItem.jsx | 25 +++ .../src/components/AuctionItem.module.css | 40 +++++ frontends/03-frontend/src/components/Bid.jsx | 59 +++++++ .../03-frontend/src/components/Bid.module.css | 54 +++++++ .../03-frontend/src/components/LastBid.jsx | 16 ++ .../src/components/LastBid.module.css | 29 ++++ .../03-frontend/src/components/Navigation.jsx | 37 +++++ .../components/Skeletons/Skeleton.module.css | 147 ++++++++++++++++++ .../Skeletons/SkeletonAuctionItem.jsx | 22 +++ .../src/components/Skeletons/SkeletonBid.jsx | 19 +++ .../components/Skeletons/SkeletonTimer.jsx | 24 +++ .../03-frontend/src/components/Timer.jsx | 74 +++++++++ .../src/components/Timer.module.css | 33 ++++ frontends/03-frontend/src/config.js | 1 + frontends/03-frontend/src/context.js | 13 ++ frontends/03-frontend/src/pages/_app.js | 27 ++++ .../src/pages/api/getBidHistory.js | 44 ++++++ frontends/03-frontend/src/pages/index.js | 139 +++++++++++++++++ .../03-frontend/src/styles/app.module.css | 93 +++++++++++ frontends/03-frontend/src/styles/globals.css | 95 +++++++++++ frontends/03-frontend/src/wallets/near.js | 142 +++++++++++++++++ 62 files changed, 1272 insertions(+), 1 deletion(-) rename {frontend => frontends/01-frontend}/.eslintrc.json (100%) rename {frontend => frontends/01-frontend}/README.md (100%) rename {frontend => frontends/01-frontend}/jsconfig.json (100%) rename {frontend => frontends/01-frontend}/next.config.js (100%) rename {frontend => frontends/01-frontend}/package.json (95%) rename {frontend => frontends/01-frontend}/public/favicon.ico (100%) rename {frontend => frontends/01-frontend}/public/near-logo.svg (100%) rename {frontend => frontends/01-frontend}/public/near.svg (100%) rename {frontend => frontends/01-frontend}/public/next.svg (100%) rename {frontend => frontends/01-frontend}/public/vercel.svg (100%) rename {frontend => frontends/01-frontend}/src/components/AuctionItem.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/AuctionItem.module.css (100%) rename {frontend => frontends/01-frontend}/src/components/Bid.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/Bid.module.css (100%) rename {frontend => frontends/01-frontend}/src/components/LastBid.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/LastBid.module.css (100%) rename {frontend => frontends/01-frontend}/src/components/Navigation.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/Skeletons/Skeleton.module.css (100%) rename {frontend => frontends/01-frontend}/src/components/Skeletons/SkeletonAuctionItem.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/Skeletons/SkeletonBid.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/Skeletons/SkeletonTimer.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/Timer.jsx (100%) rename {frontend => frontends/01-frontend}/src/components/Timer.module.css (100%) rename {frontend => frontends/01-frontend}/src/config.js (100%) rename {frontend => frontends/01-frontend}/src/context.js (100%) rename {frontend => frontends/01-frontend}/src/pages/_app.js (100%) rename {frontend => frontends/01-frontend}/src/pages/api/getBidHistory.js (100%) rename {frontend => frontends/01-frontend}/src/pages/index.js (100%) rename {frontend => frontends/01-frontend}/src/styles/app.module.css (100%) rename {frontend => frontends/01-frontend}/src/styles/globals.css (100%) rename {frontend => frontends/01-frontend}/src/wallets/near.js (100%) create mode 100644 frontends/03-frontend/.eslintrc.json create mode 100644 frontends/03-frontend/README.md create mode 100644 frontends/03-frontend/jsconfig.json create mode 100644 frontends/03-frontend/next.config.js create mode 100644 frontends/03-frontend/package.json create mode 100644 frontends/03-frontend/public/favicon.ico create mode 100644 frontends/03-frontend/public/near-logo.svg create mode 100644 frontends/03-frontend/public/near.svg create mode 100644 frontends/03-frontend/public/next.svg create mode 100644 frontends/03-frontend/public/vercel.svg create mode 100644 frontends/03-frontend/src/components/AuctionItem.jsx create mode 100644 frontends/03-frontend/src/components/AuctionItem.module.css create mode 100644 frontends/03-frontend/src/components/Bid.jsx create mode 100644 frontends/03-frontend/src/components/Bid.module.css create mode 100644 frontends/03-frontend/src/components/LastBid.jsx create mode 100644 frontends/03-frontend/src/components/LastBid.module.css create mode 100644 frontends/03-frontend/src/components/Navigation.jsx create mode 100644 frontends/03-frontend/src/components/Skeletons/Skeleton.module.css create mode 100644 frontends/03-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx create mode 100644 frontends/03-frontend/src/components/Skeletons/SkeletonBid.jsx create mode 100644 frontends/03-frontend/src/components/Skeletons/SkeletonTimer.jsx create mode 100644 frontends/03-frontend/src/components/Timer.jsx create mode 100644 frontends/03-frontend/src/components/Timer.module.css create mode 100644 frontends/03-frontend/src/config.js create mode 100644 frontends/03-frontend/src/context.js create mode 100644 frontends/03-frontend/src/pages/_app.js create mode 100644 frontends/03-frontend/src/pages/api/getBidHistory.js create mode 100644 frontends/03-frontend/src/pages/index.js create mode 100644 frontends/03-frontend/src/styles/app.module.css create mode 100644 frontends/03-frontend/src/styles/globals.css create mode 100644 frontends/03-frontend/src/wallets/near.js diff --git a/frontend/.eslintrc.json b/frontends/01-frontend/.eslintrc.json similarity index 100% rename from frontend/.eslintrc.json rename to frontends/01-frontend/.eslintrc.json diff --git a/frontend/README.md b/frontends/01-frontend/README.md similarity index 100% rename from frontend/README.md rename to frontends/01-frontend/README.md diff --git a/frontend/jsconfig.json b/frontends/01-frontend/jsconfig.json similarity index 100% rename from frontend/jsconfig.json rename to frontends/01-frontend/jsconfig.json diff --git a/frontend/next.config.js b/frontends/01-frontend/next.config.js similarity index 100% rename from frontend/next.config.js rename to frontends/01-frontend/next.config.js diff --git a/frontend/package.json b/frontends/01-frontend/package.json similarity index 95% rename from frontend/package.json rename to frontends/01-frontend/package.json index c1412c0c..394b15bf 100644 --- a/frontend/package.json +++ b/frontends/01-frontend/package.json @@ -1,5 +1,5 @@ { - "name": "hello-near", + "name": "auction-frontend", "version": "1.0.0", "private": true, "engines": { diff --git a/frontend/public/favicon.ico b/frontends/01-frontend/public/favicon.ico similarity index 100% rename from frontend/public/favicon.ico rename to frontends/01-frontend/public/favicon.ico diff --git a/frontend/public/near-logo.svg b/frontends/01-frontend/public/near-logo.svg similarity index 100% rename from frontend/public/near-logo.svg rename to frontends/01-frontend/public/near-logo.svg diff --git a/frontend/public/near.svg b/frontends/01-frontend/public/near.svg similarity index 100% rename from frontend/public/near.svg rename to frontends/01-frontend/public/near.svg diff --git a/frontend/public/next.svg b/frontends/01-frontend/public/next.svg similarity index 100% rename from frontend/public/next.svg rename to frontends/01-frontend/public/next.svg diff --git a/frontend/public/vercel.svg b/frontends/01-frontend/public/vercel.svg similarity index 100% rename from frontend/public/vercel.svg rename to frontends/01-frontend/public/vercel.svg diff --git a/frontend/src/components/AuctionItem.jsx b/frontends/01-frontend/src/components/AuctionItem.jsx similarity index 100% rename from frontend/src/components/AuctionItem.jsx rename to frontends/01-frontend/src/components/AuctionItem.jsx diff --git a/frontend/src/components/AuctionItem.module.css b/frontends/01-frontend/src/components/AuctionItem.module.css similarity index 100% rename from frontend/src/components/AuctionItem.module.css rename to frontends/01-frontend/src/components/AuctionItem.module.css diff --git a/frontend/src/components/Bid.jsx b/frontends/01-frontend/src/components/Bid.jsx similarity index 100% rename from frontend/src/components/Bid.jsx rename to frontends/01-frontend/src/components/Bid.jsx diff --git a/frontend/src/components/Bid.module.css b/frontends/01-frontend/src/components/Bid.module.css similarity index 100% rename from frontend/src/components/Bid.module.css rename to frontends/01-frontend/src/components/Bid.module.css diff --git a/frontend/src/components/LastBid.jsx b/frontends/01-frontend/src/components/LastBid.jsx similarity index 100% rename from frontend/src/components/LastBid.jsx rename to frontends/01-frontend/src/components/LastBid.jsx diff --git a/frontend/src/components/LastBid.module.css b/frontends/01-frontend/src/components/LastBid.module.css similarity index 100% rename from frontend/src/components/LastBid.module.css rename to frontends/01-frontend/src/components/LastBid.module.css diff --git a/frontend/src/components/Navigation.jsx b/frontends/01-frontend/src/components/Navigation.jsx similarity index 100% rename from frontend/src/components/Navigation.jsx rename to frontends/01-frontend/src/components/Navigation.jsx diff --git a/frontend/src/components/Skeletons/Skeleton.module.css b/frontends/01-frontend/src/components/Skeletons/Skeleton.module.css similarity index 100% rename from frontend/src/components/Skeletons/Skeleton.module.css rename to frontends/01-frontend/src/components/Skeletons/Skeleton.module.css diff --git a/frontend/src/components/Skeletons/SkeletonAuctionItem.jsx b/frontends/01-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx similarity index 100% rename from frontend/src/components/Skeletons/SkeletonAuctionItem.jsx rename to frontends/01-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx diff --git a/frontend/src/components/Skeletons/SkeletonBid.jsx b/frontends/01-frontend/src/components/Skeletons/SkeletonBid.jsx similarity index 100% rename from frontend/src/components/Skeletons/SkeletonBid.jsx rename to frontends/01-frontend/src/components/Skeletons/SkeletonBid.jsx diff --git a/frontend/src/components/Skeletons/SkeletonTimer.jsx b/frontends/01-frontend/src/components/Skeletons/SkeletonTimer.jsx similarity index 100% rename from frontend/src/components/Skeletons/SkeletonTimer.jsx rename to frontends/01-frontend/src/components/Skeletons/SkeletonTimer.jsx diff --git a/frontend/src/components/Timer.jsx b/frontends/01-frontend/src/components/Timer.jsx similarity index 100% rename from frontend/src/components/Timer.jsx rename to frontends/01-frontend/src/components/Timer.jsx diff --git a/frontend/src/components/Timer.module.css b/frontends/01-frontend/src/components/Timer.module.css similarity index 100% rename from frontend/src/components/Timer.module.css rename to frontends/01-frontend/src/components/Timer.module.css diff --git a/frontend/src/config.js b/frontends/01-frontend/src/config.js similarity index 100% rename from frontend/src/config.js rename to frontends/01-frontend/src/config.js diff --git a/frontend/src/context.js b/frontends/01-frontend/src/context.js similarity index 100% rename from frontend/src/context.js rename to frontends/01-frontend/src/context.js diff --git a/frontend/src/pages/_app.js b/frontends/01-frontend/src/pages/_app.js similarity index 100% rename from frontend/src/pages/_app.js rename to frontends/01-frontend/src/pages/_app.js diff --git a/frontend/src/pages/api/getBidHistory.js b/frontends/01-frontend/src/pages/api/getBidHistory.js similarity index 100% rename from frontend/src/pages/api/getBidHistory.js rename to frontends/01-frontend/src/pages/api/getBidHistory.js diff --git a/frontend/src/pages/index.js b/frontends/01-frontend/src/pages/index.js similarity index 100% rename from frontend/src/pages/index.js rename to frontends/01-frontend/src/pages/index.js diff --git a/frontend/src/styles/app.module.css b/frontends/01-frontend/src/styles/app.module.css similarity index 100% rename from frontend/src/styles/app.module.css rename to frontends/01-frontend/src/styles/app.module.css diff --git a/frontend/src/styles/globals.css b/frontends/01-frontend/src/styles/globals.css similarity index 100% rename from frontend/src/styles/globals.css rename to frontends/01-frontend/src/styles/globals.css diff --git a/frontend/src/wallets/near.js b/frontends/01-frontend/src/wallets/near.js similarity index 100% rename from frontend/src/wallets/near.js rename to frontends/01-frontend/src/wallets/near.js diff --git a/frontends/03-frontend/.eslintrc.json b/frontends/03-frontend/.eslintrc.json new file mode 100644 index 00000000..bffb357a --- /dev/null +++ b/frontends/03-frontend/.eslintrc.json @@ -0,0 +1,3 @@ +{ + "extends": "next/core-web-vitals" +} diff --git a/frontends/03-frontend/README.md b/frontends/03-frontend/README.md new file mode 100644 index 00000000..9d93bae0 --- /dev/null +++ b/frontends/03-frontend/README.md @@ -0,0 +1,45 @@ +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.js`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. + +## Learn More about NEAR + +To learn more about NEAR, take a look at the following resources: + +- [NEAR Documentation](https://docs.near.org) - learn about NEAR. +- [Frontend Docs](https://docs.near.org/build/web3-apps/quickstart) - learn about this example. + +You can check out [the NEAR repository](https://github.com/near) - your feedback and contributions are welcome! + +## Learn More about Next.js + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. diff --git a/frontends/03-frontend/jsconfig.json b/frontends/03-frontend/jsconfig.json new file mode 100644 index 00000000..b8d6842d --- /dev/null +++ b/frontends/03-frontend/jsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "paths": { + "@/*": ["./src/*"] + } + } +} diff --git a/frontends/03-frontend/next.config.js b/frontends/03-frontend/next.config.js new file mode 100644 index 00000000..91ef62f0 --- /dev/null +++ b/frontends/03-frontend/next.config.js @@ -0,0 +1,6 @@ +/** @type {import('next').NextConfig} */ +const nextConfig = { + reactStrictMode: true, +}; + +module.exports = nextConfig; diff --git a/frontends/03-frontend/package.json b/frontends/03-frontend/package.json new file mode 100644 index 00000000..394b15bf --- /dev/null +++ b/frontends/03-frontend/package.json @@ -0,0 +1,31 @@ +{ + "name": "auction-frontend", + "version": "1.0.0", + "private": true, + "engines": { + "node": ">=18" + }, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "next lint" + }, + "dependencies": { + "@near-wallet-selector/core": "^8.9.11", + "@near-wallet-selector/here-wallet": "^8.9.11", + "@near-wallet-selector/modal-ui": "^8.9.11", + "@near-wallet-selector/my-near-wallet": "^8.9.11", + "bootstrap": "^5", + "bootstrap-icons": "^1.11.3", + "near-api-js": "^4.0.3", + "next": "14.2.3", + "react": "^18", + "react-dom": "^18", + "react-toastify": "^10.0.5" + }, + "devDependencies": { + "eslint": "^8", + "eslint-config-next": "14.2.3" + } +} diff --git a/frontends/03-frontend/public/favicon.ico b/frontends/03-frontend/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..e61f7869c31c5258fe424679a0a8879603dea778 GIT binary patch literal 908 zcmV;719SX|P)5QaZ=0*}BEGJ-~E7YH;#(Fp>$3j~?K$OHwtfJbmQP&QCEU>{_mHcg2<>W*T5 zfKL{wyH6+nA|=DU9PkDVfWK)M;B@`{Gw1-k0B2w%Gl%coC%x2O?BZ6@6OR`#Y(K(I zFNqGx0UD9x{}s>9!DV&`TW6h3fl^ogwvAkGhj&4z=m3pKTE!kX!ZbOQ0Psx24;7M2 z$x$AEaq@FJo2QqSr1ZnK8Gr{~%~M=fN$INt@a*FZU16*y06cY_;lQ@iHvv%78D5oQ zRt4ax;|!f*yy^fvw$7kc`eFdmlUg}Ls`N*u2OL!Z!}a86!N4AUXXrEHbk}1|0F;zp zry?6;fS6k>Wt! z4BiWZ_N1MmI)G>5SFy|JJ4@3UssdR0P^n};PsCQ;GAxwnb7N}QoO0B4l^EQjv*fvNzkNkKpG*yaqEFTn4D6aNH0HifRE58xZ{x3K>okpJau z@6wN{XJZxTzNi>zqx55vI>W;Ro*H1F{5@SQ + + + + + + diff --git a/frontends/03-frontend/public/near.svg b/frontends/03-frontend/public/near.svg new file mode 100644 index 00000000..acec6fc1 --- /dev/null +++ b/frontends/03-frontend/public/near.svg @@ -0,0 +1 @@ + diff --git a/frontends/03-frontend/public/next.svg b/frontends/03-frontend/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/frontends/03-frontend/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontends/03-frontend/public/vercel.svg b/frontends/03-frontend/public/vercel.svg new file mode 100644 index 00000000..d2f84222 --- /dev/null +++ b/frontends/03-frontend/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontends/03-frontend/src/components/AuctionItem.jsx b/frontends/03-frontend/src/components/AuctionItem.jsx new file mode 100644 index 00000000..3703d84f --- /dev/null +++ b/frontends/03-frontend/src/components/AuctionItem.jsx @@ -0,0 +1,25 @@ +import styles from './AuctionItem.module.css'; + +const AuctionItem = ({ nftMetadata, validAuction }) => { + const cardImage = nftMetadata?.media + ? `https://image-cache-service-z3w7d7dnea-ew.a.run.app/media?url=https://arweave.net/${nftMetadata.media}` + : null; + + return ( +
+
+

{validAuction}

+

{nftMetadata?.title}

+

{nftMetadata?.description}

+
+
+ NFT +
+
+ ); +} + +export default AuctionItem; \ No newline at end of file diff --git a/frontends/03-frontend/src/components/AuctionItem.module.css b/frontends/03-frontend/src/components/AuctionItem.module.css new file mode 100644 index 00000000..e1a3a7f9 --- /dev/null +++ b/frontends/03-frontend/src/components/AuctionItem.module.css @@ -0,0 +1,40 @@ +.container { + /* border: 1px solid #ddd; */ + border-radius: 8px; + overflow: hidden; + display: flex; + flex-direction: column; + width: 400px; + margin-bottom: 2rem; +} + +.imageSection { + position: relative; +} + +.imageSection img { + width: 100%; + height: auto; +} + +.description { + padding: 1rem; + text-align: center; +} + +.stats { + margin-top: 1rem; +} + +.stats span { + font-size: 1.2rem; + color: #555; +} + + +.detail{ + text-align: center; + display: flex; + align-items: center; + justify-content: center; +} diff --git a/frontends/03-frontend/src/components/Bid.jsx b/frontends/03-frontend/src/components/Bid.jsx new file mode 100644 index 00000000..04b53c9d --- /dev/null +++ b/frontends/03-frontend/src/components/Bid.jsx @@ -0,0 +1,59 @@ +import { useContext, useEffect, useState } from 'react'; +import { NearContext } from '@/context'; +import styles from './Bid.module.css'; +import { toast } from 'react-toastify'; + +const Bid = ({ pastBids, ftName, ftImg, lastBidDisplay, ftDecimals, action}) => { + const [amount, setAmount] = useState(lastBidDisplay + 1); + const { signedAccountId } = useContext(NearContext); + + const handleBid = async () => { + if (signedAccountId) { + await action(amount); + toast("you have made a successful bid"); + } else { + toast("Please sign in to make a bid"); + } + } + + useEffect(() => { + setAmount(lastBidDisplay + 1); + } + , [lastBidDisplay]); + + return ( +
+

History

+ {typeof pastBids === 'string' ? ( +

{pastBids}

+ ) : pastBids === null ? ( +

Loading...

+ ) : pastBids.length === 0 ? ( +

No bids have been placed yet

+ ) : ( +
    + {pastBids?.map((bid, index) => ( +
  • + {bid[1] / Math.pow(10, ftDecimals)} {ftName} + {bid[0]} +
  • + ))} +
+ )} +
+ setAmount(e.target.value)} + className={styles.inputField} + /> + +
+
+ ); +} + +export default Bid; \ No newline at end of file diff --git a/frontends/03-frontend/src/components/Bid.module.css b/frontends/03-frontend/src/components/Bid.module.css new file mode 100644 index 00000000..83345dcf --- /dev/null +++ b/frontends/03-frontend/src/components/Bid.module.css @@ -0,0 +1,54 @@ +.historyContainer { + /* border: 1px solid #ddd; */ + border-radius: 8px; + padding: 1rem; + width: 400px; + text-align: center; +} + +.bidItem { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + border-bottom: 1px solid #eee; +} + +.bidItem:last-child { + border-bottom: none; +} + +.container { + display: flex; + align-items: center; + gap: 10px; /* Adds spacing between the input and button */ +} + +.inputField { + padding: 10px; + font-size: 16px; + border: 1px solid #ccc; + border-radius: 5px; + width: 100%; +} + +.bidButton { + background-color: #1679AB; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + font-size: 16px; + display: flex; + align-items: center; /* Center the icon and text vertically */ + justify-content: center; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.bidButton:hover { + background-color: #102C57; +} + +.iconFT { + margin-right: 10px; +} \ No newline at end of file diff --git a/frontends/03-frontend/src/components/LastBid.jsx b/frontends/03-frontend/src/components/LastBid.jsx new file mode 100644 index 00000000..6844dd28 --- /dev/null +++ b/frontends/03-frontend/src/components/LastBid.jsx @@ -0,0 +1,16 @@ +import styles from './LastBid.module.css'; + +const LastBid = ({lastBid, lastUpdate, ftName, ftImg, lastBidDisplay }) => { + return ( +
+
+ The last bid was {lastBidDisplay} {ftName} + {ftName} +
+ Made by {lastBid.bidder} + Refresh page in {lastUpdate} +
+ ) +} + +export default LastBid \ No newline at end of file diff --git a/frontends/03-frontend/src/components/LastBid.module.css b/frontends/03-frontend/src/components/LastBid.module.css new file mode 100644 index 00000000..9811e6e7 --- /dev/null +++ b/frontends/03-frontend/src/components/LastBid.module.css @@ -0,0 +1,29 @@ +.iconFT { + margin-right: 10px; +} + +.description { + padding: 1rem; + text-align: center; +} + +.detail { + text-align: center; + display: flex; + align-items: center; + justify-content: center; +} + +.priceSection { + padding: 1rem; + text-align: center; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +} + +.currentPrice { + font-size: 1.5rem; + color: #333; +} diff --git a/frontends/03-frontend/src/components/Navigation.jsx b/frontends/03-frontend/src/components/Navigation.jsx new file mode 100644 index 00000000..6849b8b6 --- /dev/null +++ b/frontends/03-frontend/src/components/Navigation.jsx @@ -0,0 +1,37 @@ +import Image from 'next/image'; +import Link from 'next/link'; +import { useEffect, useState, useContext } from 'react'; + +import { NearContext } from '@/context'; +import NearLogo from '/public/near-logo.svg'; + +export const Navigation = () => { + const { signedAccountId, wallet } = useContext(NearContext); + const [action, setAction] = useState(() => { }); + const [label, setLabel] = useState('Loading...'); + + useEffect(() => { + if (!wallet) return; + + if (signedAccountId) { + setAction(() => wallet.signOut); + setLabel(`Logout ${signedAccountId}`); + } else { + setAction(() => wallet.signIn); + setLabel('Login'); + } + }, [signedAccountId, wallet]); + + return ( + + ); +}; \ No newline at end of file diff --git a/frontends/03-frontend/src/components/Skeletons/Skeleton.module.css b/frontends/03-frontend/src/components/Skeletons/Skeleton.module.css new file mode 100644 index 00000000..3bead234 --- /dev/null +++ b/frontends/03-frontend/src/components/Skeletons/Skeleton.module.css @@ -0,0 +1,147 @@ +.container { + border: 1px solid #ddd; + border-radius: 8px; + overflow: hidden; + display: flex; + flex-direction: column; + width: 300px; + margin-bottom: 2rem; +} + +.imageSection {.container, .historyContainer { + border: 1px solid #ddd; + border-radius: 8px; + overflow: hidden; + display: flex; + flex-direction: column; + width: 300px; + margin-bottom: 2rem; + background-color: #f2f2f2; +} + +.imageSection { + position: relative; + padding: 1rem; +} + +.skeletonImage { + width: 100%; + height: 200px; + background-color: #e0e0e0; + border-radius: 8px; +} + +.description { + padding: 1rem; + text-align: center; +} + +.skeletonText, .skeletonStats { + height: 20px; + background-color: #e0e0e0; + margin: 10px 0; + border-radius: 4px; +} + +.skeletonStats { + height: 30px; + width: 50px; + margin: 0 auto; +} + +.priceSection { + background-color: #f7f7f7; + padding: 1rem; + text-align: center; +} + +.skeletonPrice { + height: 30px; + background-color: #e0e0e0; + margin: 10px 0; + border-radius: 4px; +} + +.skeletonButton { + height: 40px; + background-color: #e0e0e0; + margin: 10px 0; + border-radius: 4px; +} + +.timer { + display: flex; + gap: 1rem; + margin-bottom: 2rem; +} + +.timer div { + text-align: center; +} + +.timer span { + display: block; + font-size: 1.5rem; + font-weight: bold; +} + + +.skeletonTime { + height: 40px; + width: 40px; + background-color: #e0e0e0; + border-radius: 4px; +} + +.bidItem { + display: flex; + justify-content: space-between; + padding: 0.5rem 0; + border-bottom: 1px solid #eee; +} + +.bidItem:last-child { + border-bottom: none; +} + position: relative; +} + +.imageSection img { + width: 100%; + height: auto; +} + +.description { + padding: 1rem; + text-align: center; +} + +.stats { + margin-top: 1rem; +} + +.stats span { + font-size: 1.2rem; + color: #555; +} + +.priceSection { + background-color: #f7f7f7; + padding: 1rem; + text-align: center; +} + +.currentPrice { + font-size: 1.5rem; + color: #333; +} + +.bidButton { + background-color: #ff9800; + color: white; + border: none; + padding: 0.5rem 1rem; + margin-top: 1rem; + cursor: pointer; + border-radius: 4px; +} diff --git a/frontends/03-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx b/frontends/03-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx new file mode 100644 index 00000000..308faddc --- /dev/null +++ b/frontends/03-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx @@ -0,0 +1,22 @@ +import styles from './Skeleton.module.css'; + +const SkeletonAuctionItem = () => { + return ( +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ); +} + +export default SkeletonAuctionItem; \ No newline at end of file diff --git a/frontends/03-frontend/src/components/Skeletons/SkeletonBid.jsx b/frontends/03-frontend/src/components/Skeletons/SkeletonBid.jsx new file mode 100644 index 00000000..d86194ba --- /dev/null +++ b/frontends/03-frontend/src/components/Skeletons/SkeletonBid.jsx @@ -0,0 +1,19 @@ +import styles from './Skeleton.module.css'; + +const SkeletonBid = () => { + return ( +
+

History

+
    + {Array.from({ length: 5 }).map((_, index) => ( +
  • +
    +
    +
  • + ))} +
+
+ ); +} + +export default SkeletonBid; diff --git a/frontends/03-frontend/src/components/Skeletons/SkeletonTimer.jsx b/frontends/03-frontend/src/components/Skeletons/SkeletonTimer.jsx new file mode 100644 index 00000000..9f3391c8 --- /dev/null +++ b/frontends/03-frontend/src/components/Skeletons/SkeletonTimer.jsx @@ -0,0 +1,24 @@ +import styles from './Skeleton.module.css'; + +const SkeletonTimer = () => { + return ( +
+
+ 99 Days +
+
+ 99 Hours +
+
+ 99 Minutes +
+
+ 99 Seconds +
+
+ + + ); +} + +export default SkeletonTimer; \ No newline at end of file diff --git a/frontends/03-frontend/src/components/Timer.jsx b/frontends/03-frontend/src/components/Timer.jsx new file mode 100644 index 00000000..922fb286 --- /dev/null +++ b/frontends/03-frontend/src/components/Timer.jsx @@ -0,0 +1,74 @@ +import { useEffect, useState } from 'react'; +import styles from './Timer.module.css'; +import { toast } from 'react-toastify'; + +const Timer = ({ endTime, claimed, action }) => { + const claim = async() =>{ + await action(); + toast("Congratulations!!") + }; + + const [time, setTime] = useState((Number(endTime) / 10 ** 6) - Date.now()); + useEffect(() => { + const timer = setInterval(() => { + setTime((prevTime) => { + const newTime = prevTime - 1000; + if (newTime <= 0) { + clearInterval(timer); + return 0; + } + return newTime; + }); + }, 1000); + + return () => clearInterval(timer); + }, []); + + const formatTime = (time) => { + const allSeconds = Math.floor(time / 1000); + const days = Math.floor(allSeconds / (3600 * 24)); + const hours = Math.floor((allSeconds % (3600 * 24)) / 3600); + const minutes = Math.floor((allSeconds % 3600) / 60); + const seconds = allSeconds % 60; + + return { allSeconds, days, hours, minutes, seconds }; + }; + + const { allSeconds, days, hours, minutes, seconds } = formatTime(time); + + const showCounter = !claimed && allSeconds > 0 + const showActionButton = !claimed && allSeconds <=0 + return ( + <> + {claimed &&
+

Auction has been claimed!

+
} + {showCounter && ( +
+
+ {String(days).padStart(2, '0')} Days +
+
+ {String(hours).padStart(2, '0')} Hours +
+
+ {String(minutes).padStart(2, '0')} Minutes +
+
+ {String(seconds).padStart(2, '0')} Seconds +
+
+ )} + {showActionButton && +
+ +
+ } + + + ); +}; + +export default Timer; \ No newline at end of file diff --git a/frontends/03-frontend/src/components/Timer.module.css b/frontends/03-frontend/src/components/Timer.module.css new file mode 100644 index 00000000..3e54b668 --- /dev/null +++ b/frontends/03-frontend/src/components/Timer.module.css @@ -0,0 +1,33 @@ +.timer { + display: flex; + gap: 1rem; + margin-bottom: 2rem; +} + +.timer div { + text-align: center; +} + +.timer span { + display: block; + font-size: 1.5rem; + font-weight: bold; +} + +.button { + background-color: #1679AB; + color: white; + border: none; + padding: 10px 20px; + border-radius: 5px; + font-size: 16px; + display: flex; + align-items: center; /* Center the icon and text vertically */ + justify-content: center; + cursor: pointer; + transition: background-color 0.3s ease; +} + +.button:hover { + background-color: #102C57; +} \ No newline at end of file diff --git a/frontends/03-frontend/src/config.js b/frontends/03-frontend/src/config.js new file mode 100644 index 00000000..198b9451 --- /dev/null +++ b/frontends/03-frontend/src/config.js @@ -0,0 +1 @@ +export const AUCTION_CONTRACT = "auction-example.testnet"; // Replace with your contract name \ No newline at end of file diff --git a/frontends/03-frontend/src/context.js b/frontends/03-frontend/src/context.js new file mode 100644 index 00000000..74223096 --- /dev/null +++ b/frontends/03-frontend/src/context.js @@ -0,0 +1,13 @@ +import { createContext } from 'react'; + +/** + * @typedef NearContext + * @property {import('./wallets/near').Wallet} wallet Current wallet + * @property {string} signedAccountId The AccountId of the signed user + */ + +/** @type {import ('react').Context} */ +export const NearContext = createContext({ + wallet: undefined, + signedAccountId: '' +}); \ No newline at end of file diff --git a/frontends/03-frontend/src/pages/_app.js b/frontends/03-frontend/src/pages/_app.js new file mode 100644 index 00000000..c7ea05a3 --- /dev/null +++ b/frontends/03-frontend/src/pages/_app.js @@ -0,0 +1,27 @@ +import { useEffect, useState } from 'react'; + +import '@/styles/globals.css'; +import { NearContext } from '@/context'; +import { Navigation } from '@/components/Navigation'; + +import { Wallet } from '@/wallets/near'; +import { NetworkId, AuctionContract } from '@/config'; + +import { ToastContainer } from 'react-toastify'; +import 'react-toastify/dist/ReactToastify.css'; + +const wallet = new Wallet({ createAccessKeyFor: AuctionContract, networkId: NetworkId }); + +export default function MyApp({ Component, pageProps }) { + const [signedAccountId, setSignedAccountId] = useState(''); + + useEffect(() => { wallet.startUp(setSignedAccountId) }, []); + + return ( + + + + + + ); +} diff --git a/frontends/03-frontend/src/pages/api/getBidHistory.js b/frontends/03-frontend/src/pages/api/getBidHistory.js new file mode 100644 index 00000000..1dd2f4a7 --- /dev/null +++ b/frontends/03-frontend/src/pages/api/getBidHistory.js @@ -0,0 +1,44 @@ +export default async function handler(req, res) { + try { + if (!process.env.API_KEY) { + return res.status(500).json({ error: "API key not provided" }); + } + // Get all bid transactions + const { contractId, ftId } = req.query; + const bidsRes = await fetch(`https://api-testnet.nearblocks.io/v1/account/${contractId}/txns?from=${ftId}&method=ft_on_transfer&page=1&per_page=25&order=desc`, { + headers: { + 'Accept': '*/*', + 'Authorization': `Bearer ${process.env.API_KEY}` // Use your API key here + } + }); + + const bidsJson = await bidsRes.json(); + + const txns = bidsJson.txns; + let pastBids = []; + + // Loop through all bids and add valid bids to the pastBids array until 5 are found + for (let i = 0; i < txns.length; i++) { + const txn = txns[i]; + + if (txn.receipt_outcome.status) { + let args = txn.actions[0].args; + let parsedArgs = JSON.parse(args); + let amount = Number(parsedArgs.amount); + let account = parsedArgs.sender_id; + + if (pastBids.length < 5) { + pastBids.push([account, amount]); + } else { + break; + } + } + } + + // Respond with the past bids + return res.status(200).json({ pastBids }); + } catch (error) { + return res.status(500).json({ error: "Failed to fetch past bids" }); + } + } + \ No newline at end of file diff --git a/frontends/03-frontend/src/pages/index.js b/frontends/03-frontend/src/pages/index.js new file mode 100644 index 00000000..966e1d12 --- /dev/null +++ b/frontends/03-frontend/src/pages/index.js @@ -0,0 +1,139 @@ +import styles from '@/styles/app.module.css'; +import AuctionItem from '@/components/AuctionItem'; +import Timer from '@/components/Timer'; +import Bid from '@/components/Bid'; +import { useContext, useEffect, useState } from 'react'; +import SkeletonAuctionItem from '@/components/Skeletons/SkeletonAuctionItem'; +import SkeletonTimer from '@/components/Skeletons/SkeletonTimer'; +import SkeletonBid from '@/components/Skeletons/SkeletonBid'; +import { NearContext } from '@/context'; +import { AUCTION_CONTRACT } from '@/config'; +import LastBid from '@/components/LastBid'; + +export default function Home() { + const [auctionInfo, setAuctionInfo] = useState(null) + const [nftInfo, setNftInfo] = useState(null) + const [secondsRemaining, setSecondsRemaining] = useState(20) + const [ftContract, setFtContract] = useState("") + const [ftName, setFtName] = useState("") + const [ftImg, setFtImg] = useState("") + const [ftDecimals, setFtDecimals] = useState(0) + const [lastBidDisplay, setLastBidDisplay] = useState(0) + const [validAuction, setValidAuction] = useState("Invalid Auction") + const [pastBids, setPastBids] = useState(null) + + const { wallet } = useContext(NearContext); + + useEffect(() => { + const getInfo = async () => { + const data = await wallet.viewMethod({ + contractId: AUCTION_CONTRACT, + method: "get_auction_info", + }); + setAuctionInfo(data) + } + getInfo(); + + if (ftContract) { + fetchPastBids(); + } + + const intervalId = setInterval(() => { + getInfo(); + setSecondsRemaining(20); + }, 20000); + + const countdownIntervalId = setInterval(() => { + setSecondsRemaining(prev => (prev === 1 ? 20 : prev - 1)); + }, 1000); + + + return () => { + clearInterval(intervalId); + clearInterval(countdownIntervalId); + }; + }, []); + + useEffect(() => { + const getNftInfo = async () => { + const data = await await wallet.viewMethod({ + contractId: auctionInfo.nft_contract, + method: "nft_token", + args: { token_id: auctionInfo.token_id } + }); + setNftInfo(data) + if (data.owner_id == AUCTION_CONTRACT) { + setValidAuction("Valid Auction") + } + } + if (auctionInfo) { + getNftInfo(); + } + + }, [auctionInfo]); + + useEffect(() => { + const getFtInfo = async () => { + const ftInfo = await await wallet.viewMethod({ + contractId: auctionInfo.ft_contract, + method: "ft_metadata", + }); + setFtContract(auctionInfo.ft_contract) + setFtName(ftInfo.symbol) + setFtImg(ftInfo.icon) + setFtDecimals(ftInfo.decimals) + let bidAmount = auctionInfo.highest_bid.bid / Math.pow(10, ftInfo.decimals) + setLastBidDisplay(bidAmount) + + fetchPastBids(); + } + if (auctionInfo) { + getFtInfo(); + } + }, [auctionInfo]); + + const bid = async (amount) => { + let real_amount = amount * Math.pow(10, ftDecimals) + let response = await wallet.callMethod({ + contractId: auctionInfo.ft_contract, + method: "ft_transfer_call", + deposit: 1, + args: { "receiver_id": AUCTION_CONTRACT, "amount": String(real_amount), "msg": "" }, + gas:"300000000000000" + }) + return response + } + + const claim = async () => { + let response = await wallet.callMethod({ + contractId: AUCTION_CONTRACT, + method: "claim", + gas:"300000000000000" + }) + return response + } + + const fetchPastBids = async () => { + const response = await fetch(`/api/getBidHistory?contractId=${AUCTION_CONTRACT}&ftId=${ftContract}`); + const data = await response.json(); + if (data.error) { + setPastBids(data.error); + } else { + setPastBids(data.pastBids); + } + } + + return ( +
+
+ {!auctionInfo ? : } + {!auctionInfo ? : } +
+
+ {!auctionInfo ? : } + {!auctionInfo ? : } +
+
+ + ); +} \ No newline at end of file diff --git a/frontends/03-frontend/src/styles/app.module.css b/frontends/03-frontend/src/styles/app.module.css new file mode 100644 index 00000000..07b13bc3 --- /dev/null +++ b/frontends/03-frontend/src/styles/app.module.css @@ -0,0 +1,93 @@ +.main { + /* max-width: 1200px; */ + width: 100%; + height: 100%; + min-height: 100vh; + margin: auto 0; + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: center; + padding: 1rem; + gap: 1rem; +} + +.description { + display: inherit; + justify-content: inherit; + align-items: inherit; + font-size: 0.85rem; + max-width: var(--max-width); + width: 100%; + z-index: 2; + font-family: var(--font-mono); +} + +.code { + font-weight: 700; + font-family: var(--font-mono); +} + +.card { + padding: 1rem 1.2rem; + border-radius: var(--border-radius); + background: rgba(var(--card-rgb), 0); + border: 1px solid rgba(var(--card-border-rgb), 0); + transition: background 200ms, border 200ms; +} + +.card span { + display: inline-block; + transition: transform 200ms; +} + +.card h2 { + font-weight: 600; + margin-bottom: 0.7rem; +} + +.card p { + margin: 0; + opacity: 0.6; + font-size: 0.9rem; + line-height: 1.5; + max-width: 30ch; +} + +.leftPanel{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 4rem 0; + width: 100%; +} + +.rightPanel{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 4rem 0; + width: 100%; +} + +/* Mobile */ +@media (max-width: 700px) { + .main { + display: block; + } + + .leftPanel{ + padding: 0; + } + + .rightPanel{ + padding: 0; + } +} + +/* Tablet and Smaller Desktop */ +@media (min-width: 701px) and (max-width: 1120px) { + +} diff --git a/frontends/03-frontend/src/styles/globals.css b/frontends/03-frontend/src/styles/globals.css new file mode 100644 index 00000000..be775194 --- /dev/null +++ b/frontends/03-frontend/src/styles/globals.css @@ -0,0 +1,95 @@ +@import 'bootstrap'; +@import 'bootstrap-icons'; + +:root { + --max-width: 1100px; + --border-radius: 12px; + + --foreground-rgb: 0, 0, 0; + --background-start-rgb: 214, 219, 220; + --background-end-rgb: 255, 255, 255; + + --primary-glow: conic-gradient(from 180deg at 50% 50%, + #16abff33 0deg, + #0885ff33 55deg, + #54d6ff33 120deg, + #0071ff33 160deg, + transparent 360deg); + --secondary-glow: radial-gradient(rgba(255, 255, 255, 1), + rgba(255, 255, 255, 0)); + + --tile-start-rgb: 239, 245, 249; + --tile-end-rgb: 228, 232, 233; + --tile-border: conic-gradient(#00000080, + #00000040, + #00000030, + #00000020, + #00000010, + #00000010, + #00000080); + + --callout-rgb: 238, 240, 241; + --callout-border-rgb: 172, 175, 176; + --card-rgb: 180, 185, 188; + --card-border-rgb: 131, 134, 135; +} + +@media (prefers-color-scheme: dark) { + :root { + --foreground-rgb: 255, 255, 255; + --background-start-rgb: 0, 0, 0; + --background-end-rgb: 0, 0, 0; + + --primary-glow: radial-gradient(rgba(1, 65, 255, 0.4), rgba(1, 65, 255, 0)); + --secondary-glow: linear-gradient(to bottom right, + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0), + rgba(1, 65, 255, 0.3)); + + --tile-start-rgb: 2, 13, 46; + --tile-end-rgb: 2, 5, 19; + --tile-border: conic-gradient(#ffffff80, + #ffffff40, + #ffffff30, + #ffffff20, + #ffffff10, + #ffffff10, + #ffffff80); + + --callout-rgb: 20, 20, 20; + --callout-border-rgb: 108, 108, 108; + --card-rgb: 100, 100, 100; + --card-border-rgb: 200, 200, 200; + } +} + +* { + box-sizing: border-box; + padding: 0; + margin: 0; +} + + + +body { + color: rgb(var(--foreground-rgb)); + background: linear-gradient(to bottom, + transparent, + rgb(var(--background-end-rgb))) rgb(var(--background-start-rgb)); + font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Noto Sans, Ubuntu, Droid Sans, Helvetica Neue, sans-serif; +} + +a { + color: inherit; + text-decoration: none; +} + +@media (prefers-color-scheme: dark) { + html { + color-scheme: dark; + } +} + +body { + height-min: 100vh; +} \ No newline at end of file diff --git a/frontends/03-frontend/src/wallets/near.js b/frontends/03-frontend/src/wallets/near.js new file mode 100644 index 00000000..5c700af5 --- /dev/null +++ b/frontends/03-frontend/src/wallets/near.js @@ -0,0 +1,142 @@ +// near api js +import { providers } from 'near-api-js'; + +// wallet selector +import { distinctUntilChanged, map } from 'rxjs'; +import '@near-wallet-selector/modal-ui/styles.css'; +import { setupModal } from '@near-wallet-selector/modal-ui'; +import { setupWalletSelector } from '@near-wallet-selector/core'; +import { setupHereWallet } from '@near-wallet-selector/here-wallet'; +import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; + +const THIRTY_TGAS = '30000000000000'; +const NO_DEPOSIT = '0'; + +export class Wallet { + /** + * @constructor + * @param {Object} options - the options for the wallet + * @param {string} options.networkId - the network id to connect to + * @param {string} options.createAccessKeyFor - the contract to create an access key for + * @example + * const wallet = new Wallet({ networkId: 'testnet', createAccessKeyFor: 'contractId' }); + * wallet.startUp((signedAccountId) => console.log(signedAccountId)); + */ + constructor({ networkId = 'testnet', createAccessKeyFor = undefined }) { + this.createAccessKeyFor = createAccessKeyFor; + this.networkId = networkId; + } + + /** + * To be called when the website loads + * @param {Function} accountChangeHook - a function that is called when the user signs in or out# + * @returns {Promise} - the accountId of the signed-in user + */ + startUp = async (accountChangeHook) => { + this.selector = setupWalletSelector({ + network: this.networkId, + modules: [setupMyNearWallet(), setupHereWallet()] + }); + + const walletSelector = await this.selector; + const isSignedIn = walletSelector.isSignedIn(); + const accountId = isSignedIn ? walletSelector.store.getState().accounts[0].accountId : ''; + + walletSelector.store.observable + .pipe( + map(state => state.accounts), + distinctUntilChanged() + ) + .subscribe(accounts => { + const signedAccount = accounts.find((account) => account.active)?.accountId; + accountChangeHook(signedAccount); + }); + + return accountId; + }; + + /** + * Displays a modal to login the user + */ + signIn = async () => { + const modal = setupModal(await this.selector, { contractId: this.createAccessKeyFor }); + modal.show(); + }; + + /** + * Logout the user + */ + signOut = async () => { + const selectedWallet = await (await this.selector).wallet(); + selectedWallet.signOut(); + }; + + /** + * Makes a read-only call to a contract + * @param {Object} options - the options for the call + * @param {string} options.contractId - the contract's account id + * @param {string} options.method - the method to call + * @param {Object} options.args - the arguments to pass to the method + * @returns {Promise} - the result of the method call + */ + viewMethod = async ({ contractId, method, args = {} }) => { + const url = `https://rpc.${this.networkId}.near.org`; + const provider = new providers.JsonRpcProvider({ url }); + + let res = await provider.query({ + request_type: 'call_function', + account_id: contractId, + method_name: method, + args_base64: Buffer.from(JSON.stringify(args)).toString('base64'), + finality: 'optimistic', + }); + return JSON.parse(Buffer.from(res.result).toString()); + }; + + + /** + * Makes a call to a contract + * @param {Object} options - the options for the call + * @param {string} options.contractId - the contract's account id + * @param {string} options.method - the method to call + * @param {Object} options.args - the arguments to pass to the method + * @param {string} options.gas - the amount of gas to use + * @param {string} options.deposit - the amount of yoctoNEAR to deposit + * @returns {Promise} - the resulting transaction + */ + callMethod = async ({ contractId, method, args = {}, gas = THIRTY_TGAS, deposit = NO_DEPOSIT }) => { + // Sign a transaction with the "FunctionCall" action + const selectedWallet = await (await this.selector).wallet(); + const outcome = await selectedWallet.signAndSendTransaction({ + receiverId: contractId, + actions: [ + { + type: 'FunctionCall', + params: { + methodName: method, + args, + gas, + deposit, + }, + }, + ], + }); + + return providers.getTransactionLastResult(outcome); + }; + + /** + * Retrieves transaction result from the network + * @param {string} txhash - the transaction hash + * @returns {Promise} - the result of the transaction + */ + getTransactionResult = async (txhash) => { + const walletSelector = await this.selector; + const { network } = walletSelector.options; + const provider = new providers.JsonRpcProvider({ url: network.nodeUrl }); + + // Retrieve transaction result from the network + const transaction = await provider.txStatus(txhash, 'unnused'); + return providers.getTransactionLastResult(transaction); + }; +} \ No newline at end of file From a72dc21c20260f4c61c7935c1c9df2e92a3c12c8 Mon Sep 17 00:00:00 2001 From: PiVortex Date: Fri, 4 Oct 2024 16:13:57 +0700 Subject: [PATCH 04/11] change contract being pointed to --- frontends/01-frontend/src/config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontends/01-frontend/src/config.js b/frontends/01-frontend/src/config.js index 198b9451..202ab8a5 100644 --- a/frontends/01-frontend/src/config.js +++ b/frontends/01-frontend/src/config.js @@ -1 +1 @@ -export const AUCTION_CONTRACT = "auction-example.testnet"; // Replace with your contract name \ No newline at end of file +export const AUCTION_CONTRACT = "basic-auction-example.testnet"; // Replace with your contract name \ No newline at end of file From a2cf938dae4d3d36353cebe7f334f657f789aefd Mon Sep 17 00:00:00 2001 From: PiVortex Date: Mon, 7 Oct 2024 13:37:57 +0700 Subject: [PATCH 05/11] progress on frontend --- .../src/components/AuctionItem.jsx | 25 ----- .../src/components/AuctionItem.module.css | 40 -------- frontends/01-frontend/src/components/Bid.jsx | 17 ++-- .../01-frontend/src/components/LastBid.jsx | 7 +- .../Skeletons/SkeletonAuctionItem.jsx | 22 ----- frontends/01-frontend/src/pages/index.js | 92 ++++++------------- 6 files changed, 43 insertions(+), 160 deletions(-) delete mode 100644 frontends/01-frontend/src/components/AuctionItem.jsx delete mode 100644 frontends/01-frontend/src/components/AuctionItem.module.css delete mode 100644 frontends/01-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx diff --git a/frontends/01-frontend/src/components/AuctionItem.jsx b/frontends/01-frontend/src/components/AuctionItem.jsx deleted file mode 100644 index 3703d84f..00000000 --- a/frontends/01-frontend/src/components/AuctionItem.jsx +++ /dev/null @@ -1,25 +0,0 @@ -import styles from './AuctionItem.module.css'; - -const AuctionItem = ({ nftMetadata, validAuction }) => { - const cardImage = nftMetadata?.media - ? `https://image-cache-service-z3w7d7dnea-ew.a.run.app/media?url=https://arweave.net/${nftMetadata.media}` - : null; - - return ( -
-
-

{validAuction}

-

{nftMetadata?.title}

-

{nftMetadata?.description}

-
-
- NFT -
-
- ); -} - -export default AuctionItem; \ No newline at end of file diff --git a/frontends/01-frontend/src/components/AuctionItem.module.css b/frontends/01-frontend/src/components/AuctionItem.module.css deleted file mode 100644 index e1a3a7f9..00000000 --- a/frontends/01-frontend/src/components/AuctionItem.module.css +++ /dev/null @@ -1,40 +0,0 @@ -.container { - /* border: 1px solid #ddd; */ - border-radius: 8px; - overflow: hidden; - display: flex; - flex-direction: column; - width: 400px; - margin-bottom: 2rem; -} - -.imageSection { - position: relative; -} - -.imageSection img { - width: 100%; - height: auto; -} - -.description { - padding: 1rem; - text-align: center; -} - -.stats { - margin-top: 1rem; -} - -.stats span { - font-size: 1.2rem; - color: #555; -} - - -.detail{ - text-align: center; - display: flex; - align-items: center; - justify-content: center; -} diff --git a/frontends/01-frontend/src/components/Bid.jsx b/frontends/01-frontend/src/components/Bid.jsx index 04b53c9d..be22a35a 100644 --- a/frontends/01-frontend/src/components/Bid.jsx +++ b/frontends/01-frontend/src/components/Bid.jsx @@ -3,9 +3,10 @@ import { NearContext } from '@/context'; import styles from './Bid.module.css'; import { toast } from 'react-toastify'; -const Bid = ({ pastBids, ftName, ftImg, lastBidDisplay, ftDecimals, action}) => { +const Bid = ({pastBids, lastBid, action}) => { const [amount, setAmount] = useState(lastBidDisplay + 1); const { signedAccountId } = useContext(NearContext); + const nearMultiplier = Math.pow(10, 24) const handleBid = async () => { if (signedAccountId) { @@ -17,9 +18,9 @@ const Bid = ({ pastBids, ftName, ftImg, lastBidDisplay, ftDecimals, action}) => } useEffect(() => { - setAmount(lastBidDisplay + 1); + setAmount(lastBid + 1); } - , [lastBidDisplay]); + , [lastBid]); return (
@@ -34,7 +35,7 @@ const Bid = ({ pastBids, ftName, ftImg, lastBidDisplay, ftDecimals, action}) =>
    {pastBids?.map((bid, index) => (
  • - {bid[1] / Math.pow(10, ftDecimals)} {ftName} + {bid[1] / nearMultiplier} $NEAR {bid[0]}
  • ))} @@ -44,12 +45,14 @@ const Bid = ({ pastBids, ftName, ftImg, lastBidDisplay, ftDecimals, action}) => setAmount(e.target.value)} className={styles.inputField} - /> + > + $NEAR +
diff --git a/frontends/01-frontend/src/components/LastBid.jsx b/frontends/01-frontend/src/components/LastBid.jsx index 6844dd28..eaa2b8ac 100644 --- a/frontends/01-frontend/src/components/LastBid.jsx +++ b/frontends/01-frontend/src/components/LastBid.jsx @@ -1,13 +1,12 @@ import styles from './LastBid.module.css'; -const LastBid = ({lastBid, lastUpdate, ftName, ftImg, lastBidDisplay }) => { +const LastBid = ({lastBid, highestBidder, lastUpdate}) => { return (
- The last bid was {lastBidDisplay} {ftName} - {ftName} + The last bid was {lastBid} $NEAR
- Made by {lastBid.bidder} + Made by {highestBidder} Refresh page in {lastUpdate}
) diff --git a/frontends/01-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx b/frontends/01-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx deleted file mode 100644 index 308faddc..00000000 --- a/frontends/01-frontend/src/components/Skeletons/SkeletonAuctionItem.jsx +++ /dev/null @@ -1,22 +0,0 @@ -import styles from './Skeleton.module.css'; - -const SkeletonAuctionItem = () => { - return ( -
-
-
-
-
-
-
-
-
-
-
-
-
-
- ); -} - -export default SkeletonAuctionItem; \ No newline at end of file diff --git a/frontends/01-frontend/src/pages/index.js b/frontends/01-frontend/src/pages/index.js index 966e1d12..6f2e623b 100644 --- a/frontends/01-frontend/src/pages/index.js +++ b/frontends/01-frontend/src/pages/index.js @@ -1,5 +1,4 @@ import styles from '@/styles/app.module.css'; -import AuctionItem from '@/components/AuctionItem'; import Timer from '@/components/Timer'; import Bid from '@/components/Bid'; import { useContext, useEffect, useState } from 'react'; @@ -11,32 +10,40 @@ import { AUCTION_CONTRACT } from '@/config'; import LastBid from '@/components/LastBid'; export default function Home() { - const [auctionInfo, setAuctionInfo] = useState(null) - const [nftInfo, setNftInfo] = useState(null) + const [highestBid, setHighestBid] = useState(null) + const [highestBidder, setHighestBidder] = useState(null) + const [claimed, setClaimed] = useState(false) + const [auctionEndTime, setAuctionEndTime] = useState(null) const [secondsRemaining, setSecondsRemaining] = useState(20) - const [ftContract, setFtContract] = useState("") - const [ftName, setFtName] = useState("") - const [ftImg, setFtImg] = useState("") - const [ftDecimals, setFtDecimals] = useState(0) - const [lastBidDisplay, setLastBidDisplay] = useState(0) - const [validAuction, setValidAuction] = useState("Invalid Auction") const [pastBids, setPastBids] = useState(null) + const nearMultiplier = Math.pow(10, 24) const { wallet } = useContext(NearContext); useEffect(() => { const getInfo = async () => { - const data = await wallet.viewMethod({ + const highestBidData = await wallet.viewMethod({ contractId: AUCTION_CONTRACT, - method: "get_auction_info", + method: "get_highest_bid", }); - setAuctionInfo(data) + setHighestBid(highestBidData.bid / nearMultiplier) + setHighestBidder(highestBidData.bidder) + + const claimedData = await wallet.viewMethod({ + contractId: AUCTION_CONTRACT, + method: "get_claimed", + }); + setClaimed(claimedData) + + const auctionEndTimeData = await wallet.viewMethod({ + contractId: AUCTION_CONTRACT, + method: "get_auction_end_time", + }); + setAuctionEndTime(auctionEndTimeData) } getInfo(); - if (ftContract) { - fetchPastBids(); - } + fetchPastBids(); const intervalId = setInterval(() => { getInfo(); @@ -54,51 +61,13 @@ export default function Home() { }; }, []); - useEffect(() => { - const getNftInfo = async () => { - const data = await await wallet.viewMethod({ - contractId: auctionInfo.nft_contract, - method: "nft_token", - args: { token_id: auctionInfo.token_id } - }); - setNftInfo(data) - if (data.owner_id == AUCTION_CONTRACT) { - setValidAuction("Valid Auction") - } - } - if (auctionInfo) { - getNftInfo(); - } - - }, [auctionInfo]); - - useEffect(() => { - const getFtInfo = async () => { - const ftInfo = await await wallet.viewMethod({ - contractId: auctionInfo.ft_contract, - method: "ft_metadata", - }); - setFtContract(auctionInfo.ft_contract) - setFtName(ftInfo.symbol) - setFtImg(ftInfo.icon) - setFtDecimals(ftInfo.decimals) - let bidAmount = auctionInfo.highest_bid.bid / Math.pow(10, ftInfo.decimals) - setLastBidDisplay(bidAmount) - - fetchPastBids(); - } - if (auctionInfo) { - getFtInfo(); - } - }, [auctionInfo]); - const bid = async (amount) => { - let real_amount = amount * Math.pow(10, ftDecimals) + let real_amount = amount * nearMultiplier let response = await wallet.callMethod({ - contractId: auctionInfo.ft_contract, - method: "ft_transfer_call", - deposit: 1, - args: { "receiver_id": AUCTION_CONTRACT, "amount": String(real_amount), "msg": "" }, + contractId: AUCTION_CONTRACT, + method: "bid", + deposit: real_amount, + args: {}, gas:"300000000000000" }) return response @@ -126,12 +95,11 @@ export default function Home() { return (
- {!auctionInfo ? : } - {!auctionInfo ? : } + {!auctionInfo ? : }
- {!auctionInfo ? : } - {!auctionInfo ? : } + {!auctionInfo ? : } + {!auctionInfo ? : }
From 97c68f269bd82463e0409f0e28c7d49f77bf2410 Mon Sep 17 00:00:00 2001 From: PiVortex Date: Mon, 7 Oct 2024 14:57:02 +0700 Subject: [PATCH 06/11] finish frontend --- frontends/01-frontend/src/components/Bid.jsx | 6 ++-- .../01-frontend/src/components/Timer.jsx | 31 ++++++++++--------- .../src/pages/api/getBidHistory.js | 14 ++++----- frontends/01-frontend/src/pages/index.js | 9 +++--- .../03-frontend/src/components/Timer.jsx | 31 ++++++++++--------- .../src/pages/api/getBidHistory.js | 2 +- 6 files changed, 47 insertions(+), 46 deletions(-) diff --git a/frontends/01-frontend/src/components/Bid.jsx b/frontends/01-frontend/src/components/Bid.jsx index be22a35a..20493b8c 100644 --- a/frontends/01-frontend/src/components/Bid.jsx +++ b/frontends/01-frontend/src/components/Bid.jsx @@ -4,7 +4,7 @@ import styles from './Bid.module.css'; import { toast } from 'react-toastify'; const Bid = ({pastBids, lastBid, action}) => { - const [amount, setAmount] = useState(lastBidDisplay + 1); + const [amount, setAmount] = useState(lastBid + 1); const { signedAccountId } = useContext(NearContext); const nearMultiplier = Math.pow(10, 24) @@ -48,9 +48,7 @@ const Bid = ({pastBids, lastBid, action}) => { min={lastBid} onChange={(e) => setAmount(e.target.value)} className={styles.inputField} - > - $NEAR - + /> diff --git a/frontends/01-frontend/src/components/Timer.jsx b/frontends/01-frontend/src/components/Timer.jsx index 922fb286..4e69066b 100644 --- a/frontends/01-frontend/src/components/Timer.jsx +++ b/frontends/01-frontend/src/components/Timer.jsx @@ -44,20 +44,23 @@ const Timer = ({ endTime, claimed, action }) => {

Auction has been claimed!

} {showCounter && ( -
-
- {String(days).padStart(2, '0')} Days -
-
- {String(hours).padStart(2, '0')} Hours -
-
- {String(minutes).padStart(2, '0')} Minutes -
-
- {String(seconds).padStart(2, '0')} Seconds -
-
+
+

Time Remaining:

+
+
+ {String(days).padStart(2, '0')} Days +
+
+ {String(hours).padStart(2, '0')} Hours +
+
+ {String(minutes).padStart(2, '0')} Minutes +
+
+ {String(seconds).padStart(2, '0')} Seconds +
+
+
)} {showActionButton &&
diff --git a/frontends/01-frontend/src/pages/api/getBidHistory.js b/frontends/01-frontend/src/pages/api/getBidHistory.js index 1dd2f4a7..61171667 100644 --- a/frontends/01-frontend/src/pages/api/getBidHistory.js +++ b/frontends/01-frontend/src/pages/api/getBidHistory.js @@ -4,11 +4,11 @@ export default async function handler(req, res) { return res.status(500).json({ error: "API key not provided" }); } // Get all bid transactions - const { contractId, ftId } = req.query; - const bidsRes = await fetch(`https://api-testnet.nearblocks.io/v1/account/${contractId}/txns?from=${ftId}&method=ft_on_transfer&page=1&per_page=25&order=desc`, { + const { contractId } = req.query; + const bidsRes = await fetch(`https://api-testnet.nearblocks.io/v1/account/${contractId}/txns?method=bid&page=1&per_page=25&order=desc`, { headers: { 'Accept': '*/*', - 'Authorization': `Bearer ${process.env.API_KEY}` // Use your API key here + 'Authorization': `Bearer ${process.env.API_KEY}` } }); @@ -22,11 +22,9 @@ export default async function handler(req, res) { const txn = txns[i]; if (txn.receipt_outcome.status) { - let args = txn.actions[0].args; - let parsedArgs = JSON.parse(args); - let amount = Number(parsedArgs.amount); - let account = parsedArgs.sender_id; - + let amount = txn.actions[0].deposit; + let account = txn.predecessor_account_id + if (pastBids.length < 5) { pastBids.push([account, amount]); } else { diff --git a/frontends/01-frontend/src/pages/index.js b/frontends/01-frontend/src/pages/index.js index 6f2e623b..da0c9718 100644 --- a/frontends/01-frontend/src/pages/index.js +++ b/frontends/01-frontend/src/pages/index.js @@ -2,7 +2,6 @@ import styles from '@/styles/app.module.css'; import Timer from '@/components/Timer'; import Bid from '@/components/Bid'; import { useContext, useEffect, useState } from 'react'; -import SkeletonAuctionItem from '@/components/Skeletons/SkeletonAuctionItem'; import SkeletonTimer from '@/components/Skeletons/SkeletonTimer'; import SkeletonBid from '@/components/Skeletons/SkeletonBid'; import { NearContext } from '@/context'; @@ -83,7 +82,7 @@ export default function Home() { } const fetchPastBids = async () => { - const response = await fetch(`/api/getBidHistory?contractId=${AUCTION_CONTRACT}&ftId=${ftContract}`); + const response = await fetch(`/api/getBidHistory?contractId=${AUCTION_CONTRACT}`); const data = await response.json(); if (data.error) { setPastBids(data.error); @@ -95,11 +94,11 @@ export default function Home() { return (
- {!auctionInfo ? : } + {!highestBid ? : }
- {!auctionInfo ? : } - {!auctionInfo ? : } + {!auctionEndTime ? : } + {!highestBidder ? : }
diff --git a/frontends/03-frontend/src/components/Timer.jsx b/frontends/03-frontend/src/components/Timer.jsx index 922fb286..4e69066b 100644 --- a/frontends/03-frontend/src/components/Timer.jsx +++ b/frontends/03-frontend/src/components/Timer.jsx @@ -44,20 +44,23 @@ const Timer = ({ endTime, claimed, action }) => {

Auction has been claimed!

} {showCounter && ( -
-
- {String(days).padStart(2, '0')} Days -
-
- {String(hours).padStart(2, '0')} Hours -
-
- {String(minutes).padStart(2, '0')} Minutes -
-
- {String(seconds).padStart(2, '0')} Seconds -
-
+
+

Time Remaining:

+
+
+ {String(days).padStart(2, '0')} Days +
+
+ {String(hours).padStart(2, '0')} Hours +
+
+ {String(minutes).padStart(2, '0')} Minutes +
+
+ {String(seconds).padStart(2, '0')} Seconds +
+
+
)} {showActionButton &&
diff --git a/frontends/03-frontend/src/pages/api/getBidHistory.js b/frontends/03-frontend/src/pages/api/getBidHistory.js index 1dd2f4a7..a7298019 100644 --- a/frontends/03-frontend/src/pages/api/getBidHistory.js +++ b/frontends/03-frontend/src/pages/api/getBidHistory.js @@ -8,7 +8,7 @@ export default async function handler(req, res) { const bidsRes = await fetch(`https://api-testnet.nearblocks.io/v1/account/${contractId}/txns?from=${ftId}&method=ft_on_transfer&page=1&per_page=25&order=desc`, { headers: { 'Accept': '*/*', - 'Authorization': `Bearer ${process.env.API_KEY}` // Use your API key here + 'Authorization': `Bearer ${process.env.API_KEY}` } }); From 052c1d944280e5f50b659811d59c1b49b836feab Mon Sep 17 00:00:00 2001 From: PiVortex Date: Mon, 7 Oct 2024 15:33:33 +0700 Subject: [PATCH 07/11] remove logging --- contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js b/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js index 6ccb346d..58f2ed36 100644 --- a/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js +++ b/contract-ts/02-winner-gets-nft/sandbox-test/main.ava.js @@ -98,9 +98,7 @@ test("Test full contract", async (t) => { // Checks that the auctioneer has the correct balance const contractNewBalance = await auctioneer.balance(); - console.log("contractNewBalance", contractNewBalance); const new_available = parseFloat(contractNewBalance.available.toHuman()); - console.log("new_available", new_available); t.is(new_available.toFixed(1), (available + 2).toFixed(1)); // Check highest bidder received the NFT From d48014dee85d47bf7880b9c3300116fa9df6e0db Mon Sep 17 00:00:00 2001 From: PiVortex Date: Tue, 8 Oct 2024 15:03:55 +0700 Subject: [PATCH 08/11] some more reorg --- contract-ts/01-basic-auction/src/contract.ts | 21 +++----- .../02-winner-gets-nft/src/contract.ts | 5 ++ contract-ts/03-bid-with-fts/src/contract.ts | 53 ++++++++++--------- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/contract-ts/01-basic-auction/src/contract.ts b/contract-ts/01-basic-auction/src/contract.ts index 15805837..8e7515fe 100644 --- a/contract-ts/01-basic-auction/src/contract.ts +++ b/contract-ts/01-basic-auction/src/contract.ts @@ -42,6 +42,14 @@ class AuctionContract { return NearPromise.new(lastBidder).transfer(lastBid); } + @call({}) + claim() { + assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); + assert(!this.claimed, "Auction has been claimed"); + this.claimed = true; + return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) + } + @view({}) get_highest_bid(): Bid { return this.highest_bid; @@ -52,21 +60,8 @@ class AuctionContract { return this.auction_end_time; } - @view({}) - get_auctioneer(): AccountId { - return this.auctioneer; - } - @view({}) get_claimed(): boolean { return this.claimed; } - - @call({}) - claim() { - assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); - assert(!this.claimed, "Auction has been claimed"); - this.claimed = true; - return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) - } } \ No newline at end of file diff --git a/contract-ts/02-winner-gets-nft/src/contract.ts b/contract-ts/02-winner-gets-nft/src/contract.ts index fbd11c94..e2a8e5de 100644 --- a/contract-ts/02-winner-gets-nft/src/contract.ts +++ b/contract-ts/02-winner-gets-nft/src/contract.ts @@ -58,6 +58,11 @@ class AuctionContract { get_auction_end_time(): BigInt { return this.auction_end_time; } + + @view({}) + get_auction_info(): AuctionContract { + return this; + } @call({}) claim() { diff --git a/contract-ts/03-bid-with-fts/src/contract.ts b/contract-ts/03-bid-with-fts/src/contract.ts index 9a965a5a..889ee4ee 100644 --- a/contract-ts/03-bid-with-fts/src/contract.ts +++ b/contract-ts/03-bid-with-fts/src/contract.ts @@ -29,30 +29,6 @@ class AuctionContract { this.token_id = token_id; } - @view({}) - get_highest_bid(): Bid { - return this.highest_bid; - } - - @view({}) - get_auction_end_time(): BigInt { - return this.auction_end_time; - } - - @call({}) - claim() { - assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); - assert(!this.claimed, "Auction has been claimed"); - - this.claimed = true; - - return NearPromise.new(this.nft_contract) - .functionCall("nft_transfer", JSON.stringify({ receiver_id: this.highest_bid.bidder, token_id: this.token_id }), BigInt(1), THIRTY_TGAS) - .then(NearPromise.new(this.ft_contract) - .functionCall("ft_transfer", JSON.stringify({ receiver_id: this.auctioneer, amount: this.highest_bid.bid }), BigInt(1), THIRTY_TGAS)) - .asReturn() - } - @call({}) ft_on_transfer({ sender_id, amount, msg }: { sender_id: AccountId, amount: bigint, msg: String }) { @@ -75,8 +51,37 @@ class AuctionContract { .asReturn() } + @call({}) + claim() { + assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); + assert(!this.claimed, "Auction has been claimed"); + + this.claimed = true; + + return NearPromise.new(this.nft_contract) + .functionCall("nft_transfer", JSON.stringify({ receiver_id: this.highest_bid.bidder, token_id: this.token_id }), BigInt(1), THIRTY_TGAS) + .then(NearPromise.new(this.ft_contract) + .functionCall("ft_transfer", JSON.stringify({ receiver_id: this.auctioneer, amount: this.highest_bid.bid }), BigInt(1), THIRTY_TGAS)) + .asReturn() + } + @call({ privateFunction: true }) ft_transfer_callback({ }): BigInt { return BigInt(0); } + + @view({}) + get_highest_bid(): Bid { + return this.highest_bid; + } + + @view({}) + get_auction_end_time(): BigInt { + return this.auction_end_time; + } + + @view({}) + get_auction_info(): AuctionContract { + return this; + } } \ No newline at end of file From 2e1d7b94ca978fe73d028a6768cc195eb1b8a0cd Mon Sep 17 00:00:00 2001 From: PiVortex Date: Tue, 8 Oct 2024 15:31:52 +0700 Subject: [PATCH 09/11] add get_auctioneer for ts part 1 --- contract-ts/01-basic-auction/src/contract.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/contract-ts/01-basic-auction/src/contract.ts b/contract-ts/01-basic-auction/src/contract.ts index 8e7515fe..f6609b61 100644 --- a/contract-ts/01-basic-auction/src/contract.ts +++ b/contract-ts/01-basic-auction/src/contract.ts @@ -60,6 +60,11 @@ class AuctionContract { return this.auction_end_time; } + @view({}) + get_auctioneer(): AccountId { + return this.auctioneer; + } + @view({}) get_claimed(): boolean { return this.claimed; From d804b76db4bfe8285dfadf6a003551b59b810aef Mon Sep 17 00:00:00 2001 From: PiVortex Date: Wed, 9 Oct 2024 16:16:38 +0700 Subject: [PATCH 10/11] add back network id + remove access key creation --- frontends/01-frontend/src/config.js | 3 ++- frontends/01-frontend/src/pages/_app.js | 4 ++-- frontends/03-frontend/src/config.js | 3 ++- frontends/03-frontend/src/pages/_app.js | 4 ++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/frontends/01-frontend/src/config.js b/frontends/01-frontend/src/config.js index 202ab8a5..3885a2e0 100644 --- a/frontends/01-frontend/src/config.js +++ b/frontends/01-frontend/src/config.js @@ -1 +1,2 @@ -export const AUCTION_CONTRACT = "basic-auction-example.testnet"; // Replace with your contract name \ No newline at end of file +export const AUCTION_CONTRACT = "basic-auction-example.testnet"; // Replace with your contract name +export const NetworkId = "testnet"; \ No newline at end of file diff --git a/frontends/01-frontend/src/pages/_app.js b/frontends/01-frontend/src/pages/_app.js index c7ea05a3..9d66ea65 100644 --- a/frontends/01-frontend/src/pages/_app.js +++ b/frontends/01-frontend/src/pages/_app.js @@ -5,12 +5,12 @@ import { NearContext } from '@/context'; import { Navigation } from '@/components/Navigation'; import { Wallet } from '@/wallets/near'; -import { NetworkId, AuctionContract } from '@/config'; +import { NetworkId } from '@/config'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -const wallet = new Wallet({ createAccessKeyFor: AuctionContract, networkId: NetworkId }); +const wallet = new Wallet({ networkId: NetworkId }); export default function MyApp({ Component, pageProps }) { const [signedAccountId, setSignedAccountId] = useState(''); diff --git a/frontends/03-frontend/src/config.js b/frontends/03-frontend/src/config.js index 198b9451..a6a384c2 100644 --- a/frontends/03-frontend/src/config.js +++ b/frontends/03-frontend/src/config.js @@ -1 +1,2 @@ -export const AUCTION_CONTRACT = "auction-example.testnet"; // Replace with your contract name \ No newline at end of file +export const AUCTION_CONTRACT = "auction-example.testnet"; // Replace with your contract name +export const NetworkId = "testnet"; \ No newline at end of file diff --git a/frontends/03-frontend/src/pages/_app.js b/frontends/03-frontend/src/pages/_app.js index c7ea05a3..9d66ea65 100644 --- a/frontends/03-frontend/src/pages/_app.js +++ b/frontends/03-frontend/src/pages/_app.js @@ -5,12 +5,12 @@ import { NearContext } from '@/context'; import { Navigation } from '@/components/Navigation'; import { Wallet } from '@/wallets/near'; -import { NetworkId, AuctionContract } from '@/config'; +import { NetworkId } from '@/config'; import { ToastContainer } from 'react-toastify'; import 'react-toastify/dist/ReactToastify.css'; -const wallet = new Wallet({ createAccessKeyFor: AuctionContract, networkId: NetworkId }); +const wallet = new Wallet({ networkId: NetworkId }); export default function MyApp({ Component, pageProps }) { const [signedAccountId, setSignedAccountId] = useState(''); From 91ad7a048ef3c3144db9f08d1e5c439a6f39691c Mon Sep 17 00:00:00 2001 From: PiVortex Date: Thu, 10 Oct 2024 13:59:33 +0700 Subject: [PATCH 11/11] move claim in part 2 ts --- .../02-winner-gets-nft/src/contract.ts | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/contract-ts/02-winner-gets-nft/src/contract.ts b/contract-ts/02-winner-gets-nft/src/contract.ts index e2a8e5de..a89d9137 100644 --- a/contract-ts/02-winner-gets-nft/src/contract.ts +++ b/contract-ts/02-winner-gets-nft/src/contract.ts @@ -49,6 +49,17 @@ class AuctionContract { return NearPromise.new(lastBidder).transfer(lastBid); } + @call({}) + claim() { + assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); + assert(!this.claimed, "Auction has been claimed"); + this.claimed = true; + + return NearPromise.new(this.nft_contract) + .functionCall("nft_transfer", JSON.stringify({ receiver_id: this.highest_bid.bidder, token_id: this.token_id }), BigInt(1), TWENTY_TGAS) + .then(NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid)) + } + @view({}) get_highest_bid(): Bid { return this.highest_bid; @@ -63,15 +74,4 @@ class AuctionContract { get_auction_info(): AuctionContract { return this; } - - @call({}) - claim() { - assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); - assert(!this.claimed, "Auction has been claimed"); - this.claimed = true; - - return NearPromise.new(this.nft_contract) - .functionCall("nft_transfer", JSON.stringify({ receiver_id: this.highest_bid.bidder, token_id: this.token_id }), BigInt(1), TWENTY_TGAS) - .then(NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid)) - } }