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