diff --git a/docs/protocol/storage/storage-staking.md b/docs/protocol/storage/storage-staking.md index d2862de5a3d..a5d3fe4efb3 100644 --- a/docs/protocol/storage/storage-staking.md +++ b/docs/protocol/storage/storage-staking.md @@ -21,7 +21,7 @@ On each incoming transaction that adds data. Let's walk through an example: -1. You launch [a guest book app](../../tutorials/examples/guest-book.md), deploying your app's smart contract to the account `example.near` +1. You launch [a guest book app](https://github.com/near-examples/guest-book-examples), deploying your app's smart contract to the account `example.near` 2. Visitors to your app can add messages to the guest book. This means your users will, [by default](/protocol/gas#understanding-gas-fees), pay a small gas fee to send their message to your contract. 3. When such a call comes in, NEAR will check that `example.near` has a large enough balance that it can stake an amount to cover the new storage needs. If it does not, the transaction will fail. diff --git a/docs/smart-contracts/security/storage.md b/docs/smart-contracts/security/storage.md index 3b5824b476e..cb1d5b528d1 100644 --- a/docs/smart-contracts/security/storage.md +++ b/docs/smart-contracts/security/storage.md @@ -8,7 +8,7 @@ On NEAR, your contract pays for the storage it uses. This means that the more da Let's walk through an example: -1. You launch [a guest book app](../../tutorials/examples/guest-book.md), deploying your app's smart contract to the account `example.near` +1. You launch [a guest book app](https://github.com/near-examples/guest-book-examples), deploying your app's smart contract to the account `example.near` 2. Visitors to your app can add messages to the guest book. This means your users will pay a small gas fee to **store** their message to your contract. 3. When a new message comes in, NEAR will check if `example.near` has enough balance to cover the new storage needs. If it does not, the transaction will fail. diff --git a/docs/smart-contracts/tutorials/basic-contracts.md b/docs/smart-contracts/tutorials/basic-contracts.md new file mode 100644 index 00000000000..547e161b848 --- /dev/null +++ b/docs/smart-contracts/tutorials/basic-contracts.md @@ -0,0 +1,299 @@ +--- +id: basic-contracts +title: Using our Basic Examples +description: "Learn NEAR smart contract basics through practical examples: Counter, Guest Book, Donation, Coin Flip, and Hello World." +--- + +import {CodeTabs, Language, Github} from '@site/src/components/UI/Codetabs'; +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; +import Card from '@site/src/components/UI/Card'; +import MovingForwardSupportSection from '@site/src/components/MovingForwardSupportSection'; + +We have created a selection of basic smart contracts to help you get started building Smart Contracts on NEAR. + +![img](/assets/docs/smart-contracts/tutorials/basic-contracts.png) + +These examples cover fundamental concepts such as state management, function calls, and token interactions. Each example is designed to be simple and easy to understand, making them perfect for beginners. + +:::tip + +Before tackling these examples, be sure to follow our [Quickstart Guide](../quickstart.md) + +::: + +--- + +## Examples + +
+
+ +

A simple smart contract that stores a `string` message on its state

+
+
+
+ +

A friendly counter that stores a number with methods to increment, decrement, and reset it

+
+
+
+ +

Users can sign the guest book, optionally paying `0.01 Ⓝ` to mark their messages as "premium

+
+
+
+ +

Forward NEAR tokens to a beneficiary while tracking all donations. Learn how contracts handle token transfers

+
+
+
+ +

Guess the outcome of a coin flip and earn points. Demonstrates how to handle randomness on the blockchain

+
+
+
+ +--- + +## Structure of the Examples + +All examples follow a consistent structure, making it easy to navigate between them. Each repository contains the **same smart contract** implemented in **Rust**, **Javascript**, and sometimes **Python**, along with a **simple frontend** to interact with the contract. + +```bash +┌── contract-rs # contract's code in Rust +│ ├── src # contract's code +│ ├── tests # sandbox test +│ ├── Cargo.toml +│ └── rust-toolchain.toml +├── contract-ts # contract's code in Typescript +│ ├── src # contract's code +│ ├── sandbox-test # sandbox test +│ ├── package.json +│ └── tsconfig.json +├── contract-py # contract's code in Python (some examples) +│ ├── contract.py # contract's code +│ ├── tests # sandbox test +│ ├── pyproject.toml +│ └── uv.lock +├── frontend # React + Next.JS frontend +│ ├── src # frontend's implementation +│ ├── public +│ ├── package.json +│ ├── next.config.js +│ └── jsconfig.json +└── README.md +``` + +--- + +## Frontend + +Each example includes a **Next.JS** frontend that is very simple to start: + +```bash +cd frontend +yarn +yarn dev +``` + +These frontends are useful to demonstrate how to connect a web application to NEAR, as well as how to interact with the smart contracts. + +:::tip +Each frontend connects to a **pre-deployed version of the contract**. Check `./frontend/config.js` to see which contract is being used, or change it to your own deployed contract +::: + +
+ +### NEAR Connector Hooks + +All frontends use [`near-connect-hooks`](https://www.npmjs.com/package/near-connect-hooks), which wrap the functionality of [NEAR Connector](../../web3-apps/tutorials/web-login/near-connector.md) to handle the connection between the web app and the NEAR blockchain. + +The `near-connect-hooks` expose a `NearProvider` that is used to wrap the entire application, usually in `pages/_app.js`: + +```jsx +import { NearProvider } from "near-connect-hooks"; + +export default function App({ Component, pageProps }: AppProps) { + return ( + + + + + ); +} +``` + +
+ +We can then use the **`useNearWallet` hook** within any component to access all NEAR-related functionality, such as login/logout, view and call functions, and sign transactions: + +```jsx +import { useNearWallet } from 'near-connect-hooks'; + +export default function App() { + // Login / Logout functionality + const { loading, signIn, signOut, signedAccountId } = useNearWallet(); + + // To interact with the contract + const { viewFunction, callFunction, signAndSendTransactions } = useNearWallet(); +} +``` + +--- + +## Smart Contract + +All repositories include the same smart contract implemented in different languages, including **Rust**, **Javascript**, and sometimes **Python**. + +The contracts are implemented following the latest versions of each SDK, and include sandbox tests showcasing how to properly test smart contracts in a realistic environment. + +
+ +### Testing + +Each contract includes sandbox tests that simulate real user interactions. For example, in the `Guest Book` example, the tests cover scenarios like having multiple accounts signing the guest book, including premium messages. + + + + +```bash +cd contract-ts +yarn +yarn test +``` + + + + +```bash +cd contract-rs +cargo test +``` + + + + +```bash +cd contract-py +uv run pytest +``` + + + + +
+ +### Creating an Account + +All smart contracts can be built and deployed using the `NEAR CLI`. A good first step is to always create a new NEAR account to deploy your contract: + +```bash +near create-account --useFaucet +``` + +:::tip + +Here we are using the `--useFaucet` flag to create a new account and pre-fund it with the [testnet faucet](../../faucet.md) + +::: + +
+ +### Building & Deploying + +Once you created an account to host the contract, you can build and deploy it: + + + + +```bash +cd contract-ts +npm run build +near deploy ./build/.wasm +``` + + + + +```bash +cd contract-rs +cargo near deploy build-non-reproducible-wasm +``` + + + + +```bash +cd contract-py +uvx nearc contract.py +near deploy .wasm +``` + + + + +
+ +### Interacting via CLI + +Once your contract is deployed, check the `README.md` of each repository to see the available methods you can call. + +As a general guide, the `NEAR CLI` has two main ways to interact with smart contracts: + +```bash +# Call a read-only (view) method +near view + +# Call a method that changes state +near call --useAccount + +# Call a method and attach NEAR tokens +near call --useAccount --deposit 1 +``` + +:::tip +Check each repository's README for the specific methods available in that contract. +::: + +--- + +## Moving Forward + +After exploring these basic examples, you can: + +- **Modify the contracts** - Try adding new functionality to deepen your understanding +- **Learn the fundamentals** - Check out [Contract Anatomy](../../smart-contracts/anatomy/anatomy.md) and [Storage](../../smart-contracts/anatomy/storage.md) + + + diff --git a/docs/tools/near-api.md b/docs/tools/near-api.md index 6c328f7290f..e91af2ee702 100644 --- a/docs/tools/near-api.md +++ b/docs/tools/near-api.md @@ -874,7 +874,7 @@ A smart contract exposes its methods, and making a function call that modifies s The most convenient way to interact with contracts is the `TypedContract` class. It provides full type safety for method names, arguments, and return values, especially when used together with an ABI. - For example, lets say there is a [Guestbook](/tutorials/examples/guest-book#testing-the-contract) contract deployed at `guestbook.near-examples.testnet`, and you want to add a message to it. To do that, you’d call its `add_message` method. + For example, lets say there is a [Guestbook](https://github.com/near-examples/guest-book-examples) contract deployed at `guestbook.near-examples.testnet`, and you want to add a message to it. To do that, you’d call its `add_message` method. ```js import { NEAR } from "@near-js/tokens"; @@ -1068,7 +1068,7 @@ If your use case requires strict ordering or depends on all actions succeeding t Now that we’ve created two separate keys, we need to create corresponding `Account` instances for each one. These will be used to build and send different transactions independently. - One of the transactions adds a message to the [Guestbook](/tutorials/examples/guest-book#testing-the-contract) contract, while the other increments a counter on a different contract. + One of the transactions adds a message to the [Guestbook](https://github.com/near-examples/guest-book-examples) contract, while the other increments a counter on a different contract. ```js const accountOne = new Account( @@ -1422,7 +1422,7 @@ View functions are read-only methods on a smart contract that do not modify stat - Let’s look at an example using the [Guestbook](/tutorials/examples/guest-book#testing-the-contract) contract to read how many messages are currently stored. + Let’s look at an example using the [Guestbook](https://github.com/near-examples/guest-book-examples) contract to read how many messages are currently stored. ```js import { JsonRpcProvider } from "@near-js/providers"; diff --git a/docs/tools/sdk.md b/docs/tools/sdk.md index 47ed6c4738c..112305d558f 100644 --- a/docs/tools/sdk.md +++ b/docs/tools/sdk.md @@ -103,7 +103,7 @@ Start from our [Smart Contract QuickStart Guide](../smart-contracts/quickstart.m ## Want to See Examples? -We have a section dedicated to [tutorials and examples](../tutorials/examples/guest-book.md) that will help you understand diverse use cases and how to implement them +We have a section dedicated to [tutorials and examples](../smart-contracts/tutorials/basic-contracts.md) that will help you understand diverse use cases and how to implement them :::tip diff --git a/docs/tutorials/crosswords/01-basics/00-overview.md b/docs/tutorials/crosswords/01-basics/00-overview.md deleted file mode 100644 index 639bac70a00..00000000000 --- a/docs/tutorials/crosswords/01-basics/00-overview.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -id: overview -sidebar_position: 1 -sidebar_label: Crossword Game Overview -title: Basics overview laying out what will be accomplished in this first section. -description: "Learn smart contract basics with an interactive crossword puzzle project using NEAR and Rust." ---- - -import basicCrossword from '/assets/docs/tutorials/crosswords/basics-crossword.jpg'; -import rustScary from '/assets/docs/tutorials/crosswords/rust-scary--ksart.near.png'; -import rustGood from '/assets/docs/tutorials/crosswords/rust-good--ksart.near.png'; - - -This first chapter of the crossword puzzle tutorial will introduce fundamental concepts to smart contract development in a beginner-friendly way. By the end of this chapter you'll have a proof-of-concept contract that can be interacted with via [NEAR CLI](/tools/near-cli) and a simple frontend that uses the [`near-api-js` library](https://www.npmjs.com/package/near-api-js). - -# Basics overview - -## It's not as bad as you think - -Rust is a serious systems programming language. There are pointers, lifetimes, macros, and other things that may look foreign. Don't worry if this is how you feel: - -
- Programmer looking at Rust code and looking worried. Art created by ksart.near -
Art by ksart.near
-
-
- -The good news is the Rust SDK takes care of a lot of the heavy lifting. - -We'll also have the compiler on our side, often telling us exactly what went wrong and offering suggestions. As we go through this tutorial, you'll begin to see patterns that we'll use over and over again. - -So don't worry, writing smart contracts in Rust on NEAR doesn't require a heavy engineering background. - -Programmer looking quite relieved at the Rust code from the NEAR SDK. Art created by ksart.near - -## Assumptions for this first chapter - -- There will be only one crossword puzzle with one solution. -- The user solving the crossword puzzle will not be able to know the solution. -- Only the author of the crossword puzzle smart contract can set the solution. - -## Completed project - -Here's the final code for this chapter: - -https://github.com/near-examples/crossword-tutorial-chapter-1 - -## How it works - -Basic crossword puzzle - -We'll have a rule about how to get the words in the proper order. We collect words in ascending order by number, and if there's and across and a down for a number, the across goes first. - -So in the image above, the solution will be **near nomicon ref finance**. - -Let's begin! diff --git a/docs/tutorials/crosswords/01-basics/01-set-up-skeleton.md b/docs/tutorials/crosswords/01-basics/01-set-up-skeleton.md deleted file mode 100644 index a22047638e7..00000000000 --- a/docs/tutorials/crosswords/01-basics/01-set-up-skeleton.md +++ /dev/null @@ -1,141 +0,0 @@ ---- -sidebar_position: 2 -sidebar_label: Set up Rust and a contract skeleton -title: Set up Rust, get a NEAR testnet account, NEAR CLI, and get a basic smart contract skeleton ready -description: "Set up Rust, NEAR testnet account, and CLI to create a basic smart contract skeleton." ---- -import {Github} from "@site/src/components/UI/Codetabs"; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -In this tutorial we'll get a `testnet` account, use [NEAR CLI RS](../../../tools/cli.md) to add a key to our computer's file system, and set up the basic skeleton of a Rust smart contract. - -# Getting started - -## Setting up Rust - -You may have found the [online Rust Book](https://doc.rust-lang.org/stable/book), which is a great resource for getting started with Rust. However, there are key items that are different when it comes to blockchain development. Namely, that smart contracts are [technically libraries and not binaries](https://learning-rust.github.io/docs/cargo-crates-and-basic-project-structure/#crate), but for now just know that we won't be using some commands commonly found in the Rust Book. - -:::caution -We won't be using `cargo run` during smart contract development. -::: - -Instead, we'll be iterating on our smart contract by building it and running tests. - -### Install Rust using `rustup` - -Please see the directions from the [Rustup site](https://rustup.rs/#). For OS X or Unix, you may use: - -```bash -curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -``` - -(Taken from the [Rust installation guide](https://www.rust-lang.org/tools/install)) - -### Add Wasm toolchain - -Smart contracts compile to WebAssembly (Wasm) so we'll add the toolchain for Rust. - -```bash -rustup target add wasm32-unknown-unknown -``` - -(More info on [targets and this toolchain here](https://doc.rust-lang.org/edition-guide/rust-2018/platform-and-target-support/webassembly-support.html).) - -## Getting a testnet account - -Visit [NEAR Wallet for testnet](https://testnet.mynearwallet.com/) and register for a free account. For the purposes of this tutorial, you may skip the option to add two-factor authentication if you wish. - -:::note What just happened? -When you created your NEAR `testnet` account, a private key was created and placed into your browser's local storage. You may inspect this using developer tools and see it. -::: - -## Creating a new key on your computer - -We'll want to use a command-line interface (CLI) tool to deploy a contract, but at the moment the private key only exists in the browser. Next we'll _add a new key_ to the testnet account and have this stored locally on our computer as a JSON file. (Yes, you can have multiple keys on your NEAR account, which is quite powerful!) - -Let's install [NEAR CLI RS](../../../tools/cli.md) using `cargo`. You can also download the pre-compiled version of `near-cli-rs` for your OS from [GitHub's Releases page](https://github.com/near/near-cli-rs/releases/). - -```bash -cargo install near-cli-rs -``` - -You may now run: - -```bash -near -``` - -to see various commands, which are covered [in detail here](https://github.com/near/near-cli-rs/blob/main/docs/GUIDE.en.md). - -We'll start by "logging in" with this command: - - - - - ```bash - near login - ``` - - - - - ```bash - near account import-account using-web-wallet network-config testnet - ``` - - - -This will bring you to NEAR Wallet again where you can confirm the creation of a **full-access** key. We'll get to full-access and function-call access keys later, just know that for powerful actions like "deploy" we'll need a full-access key. Follow the instructions from the login command to create a key on your hard drive. This will be located in your operating system's home directory in a folder called `.near-credentials`. - -:::note How was a key added? -When you typed `near login`, NEAR CLI generated a key pair: a private and public key. It kept the private key tucked away in a JSON file and sent the public key as a URL parameter to NEAR Wallet. The URL is long and contains other info instructing NEAR Wallet to "add a full access key" to the account. Our browser's local storage had a key (created when the account was made) that is able to do several things, including adding another key. It took the public key from the URL parameter, used it as an argument, and voilà: the `testnet` account has an additional key! -::: - -You can see the keys associated with your account by running the following command, replacing `friend.testnet` with your account name: - - - - - ```bash - near list-keys friend.testnet - ``` - - - - - ```bash - near account list-keys friend.testnet network-config testnet now - ``` - - - -## Start writing Rust! - -There's a basic repository that's helpful to clone or download [located here](https://github.com/near/boilerplate-template-rs). - -The first thing we'll do is modify the manifest file at `Cargo.toml`: - -```diff -[package] -- name = "rust-template" -+ name = "my-crossword" -version = "0.1.0" -- authors = ["Near Inc "] -+ authors = ["NEAR Friend "] -edition = "2018" -``` - -By changing the `name` here, we'll be changing the compiled Wasm file's name after running the build script. (`build.sh` for OS X and Linux, `build.bat` for Windows.) After running the build script, we can expect to find our compiled Wasm smart contract in `res/my_crossword.wasm`. - -Now let's look at our main file, in `src/lib.rs`: - - - -As you can see, this is a stub that's ready to be filled in. Let's pause and point out a few items: - -- Note the **near** macro is above the struct and the impl -- Here the main struct is called `Contract`, while in other examples it might be `Counter` or something else. This is purely stylistic, but you may learn more from the link in the previous bullet. -- You may notice the word "Borsh" and wonder what that means. This is a binary serializer. Eventually, we'll want to save data as ones and zeroes to validators' hard drives, and do it efficiently. We use Borsh for this, as is explained [on this website](https://borsh.io). - -Next, let's modify this contract little by little… diff --git a/docs/tutorials/crosswords/01-basics/02-add-functions-call.md b/docs/tutorials/crosswords/01-basics/02-add-functions-call.md deleted file mode 100644 index 29f255c542c..00000000000 --- a/docs/tutorials/crosswords/01-basics/02-add-functions-call.md +++ /dev/null @@ -1,366 +0,0 @@ ---- -sidebar_position: 3 -sidebar_label: Add basic code, create a subaccount, and call methods -title: Alter the smart contract, learning about basics of development -description: "Modify the smart contract, create a subaccount, and learn to call methods on NEAR." - ---- -import {Github} from "@site/src/components/UI/Codetabs"; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -import teachingDeployment from '/assets/docs/tutorials/crosswords/teaching--jeheycell.near--artcultureac.jpeg'; -import createAccount from '/assets/docs/tutorials/crosswords/creating account with text--seanpineda.near--_seanpineda.png'; -import chalkboardErase from '/assets/docs/tutorials/crosswords/erasing-subaccount-chalkboard--iambon.near--JohnreyBona.mp4'; - -This section will modify the smart contract skeleton from the previous section. This tutorial will start by writing a contract in a somewhat useless way in order to learn the basics. Once we've got a solid understanding, we'll iterate until we have a crossword puzzle. - -## Add a const, a field, and functions - -Let's modify the contract to be: - - - -We've done a few things here: -1. Set a constant for the puzzle number. -2. Added the field `crossword_solution` to our main struct. -3. Implemented three functions: one that's view-only and two that are mutable, meaning they have the ability to change state. -4. Used logging, which required the import of `env` from our `near_sdk` crate. - -Before moving on, let's talk about these changes and how to think about them, beginning with the constant: - -```rust -const PUZZLE_NUMBER: u8 = 1; -``` - -This is an in-memory value, meaning that when the smart contract is spun up and executed in the virtual machine, the value `1` is contained in the contract code. This differs from the next change, where a field is added to the struct containing the `#[near]` macro. The field `crossword_solution` has the type of `String` and, like any other fields added to this struct, the value will live in **persistent storage**. With NEAR, storage is "paid for" via the native NEAR token (Ⓝ). It is not "state rent" but storage staking, paid once, and returned when storage is deleted. This helps incentivize users to keep their state clean, allowing for a more healthy chain. Read more about [storage staking here](/protocol/storage/storage-staking). - -Let's now look at the three new functions: - -```rust -pub fn get_puzzle_number(&self) -> u8 { - PUZZLE_NUMBER -} -``` - -As is covered in the [function section of these docs](../../../smart-contracts/anatomy/functions.md), a "view-only" function will have open parenthesis around `&self` while "change methods" or mutable functions will have `&mut self`. In the function above, the `PUZZLE_NUMBER` is returned. A user may call this method using the proper RPC endpoint without signing any transaction, since it's read-only. Think of it like a GET request, but using RPC endpoints that are [documented here](/api/rpc/contracts#call-a-contract-function). - -Mutable functions, on the other hand, require a signed transaction. The first example is a typical approach where the user supplies a parameter that's assigned to a field: - -```rust -pub fn set_solution(&mut self, solution: String) { - self.crossword_solution = solution; -} -``` - -The next time the smart contract is called, the contract's field `crossword_solution` will have changed. - -The second example is provided for demonstration purposes: - -```rust -pub fn guess_solution(&mut self, solution: String) { - if solution == self.crossword_solution { - env::log_str("You guessed right!") - } else { - env::log_str("Try again.") - } -} -``` - -Notice how we're not saving anything to state and only logging? Why does this need to be mutable? - -Well, logging is ultimately captured inside blocks added to the blockchain. (More accurately, transactions are contained in chunks and chunks are contained in blocks. More info in the [Nomicon spec](https://nomicon.io/Architecture.html?highlight=chunk#blockchain-layer-concepts).) So while it is not changing the data in the fields of the struct, it does cost some amount of gas to log, requiring a signed transaction by an account that pays for this gas. - ---- - -## Building and deploying - -Here's what we'll want to do: - -
- Teacher shows chalkboard with instructions on how to properly deploy a smart contract. 1. Build smart contract. 2. Create a subaccount (or delete and recreate if it exists) 3. Deploy to subaccount. 4. Interact. Art created by jeheycell.near -
Art by jeheycell.near
-
- -### Build the contract - -To build the contract, we'll be using [`cargo-near`](https://github.com/near/cargo-near). - -Install `cargo-near` first: - -```bash -cargo install cargo-near -``` - -Run the following commands and expect to see the compiled Wasm file copied to the `target/near` folder. - -```bash -cd contract -cargo near build -``` - -### Create a subaccount - -If you've followed from the previous section, you have NEAR CLI installed and a full-access key on your machine. While developing, it's a best practice to create a subaccount and deploy the contract to it. This makes it easy to quickly delete and recreate the subaccount, which wipes the state swiftly and starts from scratch. Let's use NEAR CLI to create a subaccount and fund with 1 NEAR: - - - - - ```bash - near create-account crossword.friend.testnet --use-account friend.testnet --initial-balance 1 --network-id testnet - ``` - - - - - ```bash - near account create-account fund-myself crossword.friend.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -If you look again in your home directory's `.near-credentials`, you'll see a new key for the subaccount with its own key pair. This new account is, for all intents and purposes, completely distinct from the account that created it. It might as well be `alice.testnet`, as it has, by default, no special relationship with the parent account. To be clear, `friend.testnet` cannot delete or deploy to `crossword.friend.testnet` unless it's done in a single transaction using Batch Actions, which we'll cover later. - -:::info Subaccount nesting -It's possible to have the account `another.crossword.friend.testnet`, but this account must be created by `crossword.friend.testnet`. - -`friend.testnet` **cannot** create `another.crossword.friend.testnet` because accounts may only create a subaccount that's "one level deeper." - -See this visualization where two keys belonging to `mike.near` are able to create `new.mike.near`. We'll get into concepts around access keys later. - -
- Depiction of create account where two figures put together a subaccount. Art created by seanpineda.near -
Art by seanpineda.near
-
- -::: - -We won't get into top-level accounts or implicit accounts, but you may read more [about that here](/protocol/account-model). - -Now that we have a key pair for our subaccount, we can deploy the contract to `testnet` and interact with it! - -#### What's a codehash? - -We're almost ready to deploy the smart contract to the account, but first let's take a look at the account we're going to deploy to. Remember, this is the subaccount we created earlier. To view the state easily with NEAR CLI, you may run this command: - - - - - ```bash - near state crossword.friend.testnet --networkId testnet - ``` - - - - - ```bash - near account view-account-summary crossword.friend.testnet network-config testnet now - ``` - - - -What you'll see is something like this: - -```bash ------------------------------------------------------------------------------------------- - crossword.friend.testnet At block #167331831 - (Evjnf29LuqFE7FUf97VQZzNfnUgPFLNyyiUk9qr4Wjri) ------------------------------------------------------------------------------------------- - Native account balance 10.01 NEAR ------------------------------------------------------------------------------------------- - Validator stake 0 NEAR ------------------------------------------------------------------------------------------- - Storage used by the account 182 B ------------------------------------------------------------------------------------------- - Contract (SHA-256 checksum hex) No contract code ------------------------------------------------------------------------------------------- - Access keys 1 full access keys and 0 function-call-only access keys ------------------------------------------------------------------------------------------- -``` - -Note the `Contract` SHA-256 checksum is missing. This indicates that there is no contract deployed to this account. - -Let's deploy the contract (to the subaccount we created) and then check this again. - -### Deploy the contract - -Ensure that in your command line application, you're in the directory that contains the `Cargo.toml` file, then run: - -```bash -cargo near deploy build-non-reproducible-wasm crossword.friend.testnet without-init-call network-config testnet sign-with-keychain send -``` - -Congratulations, you've deployed the smart contract! Note that NEAR CLI will output a link to [NEAR Explorer](https://nearblocks.io/) where you can inspect details of the transaction. - -Lastly, let's run this command again and notice that the `Contract` has a SHA-256 checksum. This is the hash of the smart contract deployed to the account. - - - - - ```bash - near state crossword.friend.testnet --networkId testnet - ``` - - - - - ```bash - near account view-account-summary crossword.friend.testnet network-config testnet now - ``` - - - -:::note - -Deploying a contract is often done on the command line. While it may be _technically_ possible to deploy via a frontend, the CLI is likely the best approach. If you're aiming to use a factory model, (where a smart contract deploys contract code to a subaccount) this isn't covered in the tutorial, but you may reference the [contracts in SputnikDAO](https://github.com/near-daos/sputnik-dao-contract). - -::: - -### Call the contract methods (interact!) - -Let's first call the method that's view-only: - - - - - ```bash - near view crossword.friend.testnet get_puzzle_number '{}' --networkId testnet - ``` - - - - - ```bash - near contract call-function as-read-only crossword.friend.testnet get_puzzle_number json-args {} network-config testnet now - ``` - - - -Your command prompt will show the result is `1`. Since this method doesn't take any arguments, we don't pass any. - -Next, we'll add a crossword solution as a string (later we'll do this in a better way) argument: - - - - - ```bash - near call crossword.friend.testnet set_solution '{"solution": "near nomicon ref finance"}' --gas 100000000000000 --accountId friend.testnet - ``` - - - - - ```bash - near contract call-function as-transaction crossword.friend.testnet set_solution json-args '{"solution": "near nomicon ref finance"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -Note that we used NEAR CLI's [`view` command](/tools/near-cli#call), and didn't include an `--accountId` flag. As mentioned earlier, this is because we are not signing a transaction. This second method uses the NEAR CLI [`call` command](/tools/near-cli#call) which does sign a transaction and requires the user to specify a NEAR account that will sign it, using the credentials files we looked at. - -The last method we have will check the argument against what is stored in state and write a log about whether the crossword solution is correct or incorrect. - -Correct: - - - - - ```bash - near call crossword.friend.testnet guess_solution '{"solution": "near nomicon ref finance"}' --gas 100000000000000 --accountId friend.testnet - ``` - - - - - ```bash - near contract call-function as-transaction crossword.friend.testnet guess_solution json-args '{"solution": "near nomicon ref finance"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -You'll see something like this: - -![Command line shows log for successful solution guess](/assets/docs/tutorials/crosswords/cli-guess-solution.png) - -Notice the log we wrote is output as well as a link to NEAR Explorer. - -Incorrect: - - - - - ```bash - near call crossword.friend.testnet guess_solution '{"solution": "wrong answers here"}' --gas 100000000000000 --accountId friend.testnet - ``` - - - - - ```bash - near contract call-function as-transaction crossword.friend.testnet guess_solution json-args '{"solution": "wrong answers here"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -As you can imagine, the above command will show something similar, except the logs will indicate that you've given the wrong solution. - -## Reset the account's contract and state - -We'll be iterating on this smart contract during this tutorial, and in some cases it's best to start fresh with the NEAR subaccount we created. The pattern to follow is to **delete** the account (sending all remaining testnet Ⓝ to a recipient) and then **create the account** again. - - - -
-
Deleting a recreating a subaccount will clear the state and give us a fresh start.
Animation by iambon.near
-
- -Using NEAR CLI, the commands will look like this: - - - - - ```bash - # deleting an account - near delete-account crossword.friend.testnet friend.testnet --networkId testnet - - # creating an account - near create-account crossword.friend.testnet --use-account friend.testnet --initial-balance 1 --network-id testnet - ``` - - - - - ```bash - # deleting an account - near account delete-account crossword.friend.testnet beneficiary friend.testnet network-config testnet sign-with-keychain send - - # creating an account - near account create-account fund-myself crossword.friend.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -The first command deletes `crossword.friend.testnet` and sends the rest of its NEAR to `friend.testnet`. - -## Wrapping up - -So far, we're writing a simplified version of smart contract and approaching the crossword puzzle in a novice way. Remember that blockchain is an open ledger, meaning everyone can see the state of smart contracts and transactions taking place. - -:::info How would you do that? -You may hit an RPC endpoint corresponding to `view_state` and see for yourself. Note: this quick example serves as demonstration purposes, but note that the string being returned is Borsh-serialized and contains more info than just the letters. - -```bash - curl -d '{"jsonrpc": "2.0", "method": "query", "id": "see-state", "params": {"request_type": "view_state", "finality": "final", "account_id": "crossword.friend.testnet", "prefix_base64": ""}}' -H 'Content-Type: application/json' https://rpc.testnet.near.org -``` - -![Screenshot of a terminal screen showing a curl request to an RPC endpoint that returns state of a smart contract](/assets/docs/tutorials/crosswords/rpc-api-view-state.png) - -More on this RPC endpoint in the [NEAR docs](/api/rpc/contracts#view-contract-state). -::: - -In this section, we saved the crossword solution as plain text, which is likely not a great idea if we want to hide the solution to players of this crossword puzzle. Even though we don't have a function called `show_solution` that returns the struct's `crossword_solution` field, the value is stored transparently in state. We won't get into viewing contract state at this moment, but know it's rather easy [and documented here](/api/rpc/contracts#view-contract-state). - -The next section will explore hiding the answer from end users playing the crossword puzzle. diff --git a/docs/tutorials/crosswords/01-basics/03-hashing-and-unit-tests.md b/docs/tutorials/crosswords/01-basics/03-hashing-and-unit-tests.md deleted file mode 100644 index bfabaea2450..00000000000 --- a/docs/tutorials/crosswords/01-basics/03-hashing-and-unit-tests.md +++ /dev/null @@ -1,264 +0,0 @@ ---- -sidebar_position: 4 -sidebar_label: Hash the solution, unit tests, and an init method -title: Introduction to basic hashing and adding unit tests -description: "Hash the crossword solution, add unit tests, and initialize the smart contract securely." ---- -import {Github} from "@site/src/components/UI/Codetabs"; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -import batchCookieTray from '/assets/docs/tutorials/crosswords/batch-of-actions--dobulyo.near--w_artsu.jpg'; - -In the previous section, we stored the crossword solution as plain text as a `String` type on the smart contract. If we're trying to hide the solution from the users, this isn't a great approach as it'll be public to anyone looking at the state. Let's instead hash our crossword solution and store that instead. There are different ways to hash data, but let's use `sha256` which is one of the hashing algorithms available in [the Rust SDK](https://docs.rs/near-sdk/latest/near_sdk/env/fn.sha256.html). - -:::info Remind me about hashing -Without getting into much detail, hashing is a "one-way" function that will output a result from a given input. If you have input (in our case, the crossword puzzle solution) you can get a hash, but if you have a hash you cannot get the input. This basic idea is foundational to information theory and security. - -Later on in this tutorial, we'll switch from using `sha256` to using cryptographic key pairs to illustrate additional NEAR concepts. - -Learn more about hashing from [Evgeny Kapun](https://github.com/abacabadabacaba)'s presentation on the subject. You may find other NEAR-related videos from the channel linked in the screenshot below. - -[![Evgeny Kapun presents details on hashing](/assets/docs/tutorials/crosswords/kapun-hashing.png)](https://youtu.be/PfabikgnD08) -::: - -## Helper unit test during rapid iteration - -As mentioned in the first section of this **Basics** chapter, our smart contract is technically a library as defined in the manifest file. For our purposes, a consequence of writing a library in Rust is not having a "main" function that runs. You may find many online tutorials where the command `cargo run` is used during development. We don't have this luxury, but we can use unit tests to interact with our smart contract. This is likely more convenient than building the contract, deploying to a blockchain network, and calling a method. - -We'll add a dependency to the [hex crate](https://crates.io/crates/hex) to make things easier. As you may remember, dependencies live in the manifest file. - - - -Let's write a unit test that acts as a helper during development. This unit test will sha256 hash the input **"near nomicon ref finance"** and print it in a human-readable, hex format. (We'll typically put unit tests at the bottom of the `lib.rs` file.) - - - -:::info What is that `{:?}` thing? -Take a look at different formatting traits that are covered in the [`std` Rust docs](https://doc.rust-lang.org/std/fmt/index.html#formatting-traits) regarding this. This is a `Debug` formatting trait and can prove to be useful during development. -::: - -Run the unit tests with the command: - -``` -cargo test -- --nocapture -``` - -You'll see this output: - -``` -… -running 1 test -Let's debug: "69c2feb084439956193f4c21936025f14a5a5a78979d67ae34762e18a7206a0f" -test tests::debug_get_hash ... ok - -test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s -``` - -This means when you sha256 the input **"near nomicon ref finance"** it produces the hash: -`69c2feb084439956193f4c21936025f14a5a5a78979d67ae34762e18a7206a0f` - -:::tip Note on the test flags -You may also run tests using: - -``` -cargo test -``` - -Note that the test command we ran had additional flags. Those flags told Rust **not to hide the output** from the tests. You can read more about this in [the cargo docs](https://doc.rust-lang.org/cargo/commands/cargo-test.html#display-options). Go ahead and try running the tests using the command above, without the additional flags, and note that we won't see the debug message. -::: - -The unit test above is meant for debugging and quickly running snippets of code. Some may find this a useful technique when getting familiar with Rust and writing smart contracts. Next we'll write a real unit test that applies to this early version of our crossword puzzle contract. - -## Write a regular unit test - -Let's add this unit test (inside the `mod tests {}` block, under our previous unit test) and analyze it: - - - -The first few lines of code will be used commonly when writing unit tests. It uses the `VMContextBuilder` to create some basic context for a transaction, then sets up the testing environment. - -Next, an object is created representing the contract and the `set_solution` function is called. After that, the `guess_solution` function is called twice: first with the incorrect solution and then the correct one. We can check the logs to determine that the function is acting as expected. - -:::info Note on assertions -This unit test uses the [`assert_eq!`](https://doc.rust-lang.org/std/macro.assert_eq.html) macro. Similar macros like [`assert!`](https://doc.rust-lang.org/std/macro.assert.html) and [`assert_ne!`](https://doc.rust-lang.org/std/macro.assert_ne.html) are commonly used in Rust. These are great to use in unit tests. However, these will add unnecessary overhead when added to contract logic, and it's recommended to use the [`require!` macro](https://docs.rs/near-sdk/4.0.0-pre.2/near_sdk/macro.require.html). See more information on this and [other efficiency tips here](../../../smart-contracts/anatomy/reduce-size.md). -::: - -Again, we can run all the unit tests with: - -``` -cargo test -- --nocapture -``` - -:::tip Run only one test -To only run this latest test, use the command: - -``` -cargo test check_guess_solution -- --nocapture -``` - -::: - -## Modifying `set_solution` - -The [overview section](00-overview.md) of this chapter tells us we want to have a single crossword puzzle and the user solving the puzzle should not be able to know the solution. Using a hash addresses this, and we can keep `crossword_solution`'s field type, as `String` will work just fine. The overview also indicates we only want the author of the crossword puzzle to be able to set the solution. As it stands, our function `set_solution` can be called by anyone with a full-access key. It's trivial for someone to create a NEAR account and call this function, changing the solution. Let's fix that. - -Let's have the solution be set once, right after deploying the smart contract. - -Here we'll use the [`#[near]` macro](https://docs.rs/near-sdk/latest/near_sdk/attr.near.html) on a function called `new`, which is a common pattern. - - - -Let's call this method on a fresh contract. - -Go into the directory containing the Rust smart contract and build it: - -```bash -cd contract - -# Build -cargo near build -``` - -Create fresh account if you wish, which is good practice: - - - - - ```bash - # Delete an account - near delete-account crossword.friend.testnet friend.testnet --networkId testnet - - # Create an account again - near create-account crossword.friend.testnet --use-account friend.testnet --initial-balance 1 --network-id testnet - ``` - - - - - ```bash - # Delete an account - near account delete-account crossword.friend.testnet beneficiary friend.testnet network-config testnet sign-with-keychain send - - # Create an account again - near account create-account fund-myself crossword.friend.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -Deploy the contract: - -```bash -cargo near deploy build-non-reproducible-wasm crossword.friend.testnet without-init-call network-config testnet sign-with-keychain send -``` - -Call the "new" method: - - - - - ```bash - near call crossword.friend.testnet new '{"solution": "69c2feb084439956193f4c21936025f14a5a5a78979d67ae34762e18a7206a0f"}' --gas 100000000000000 --accountId crossword.friend.testnet - ``` - - - - - ```bash - near contract call-function as-transaction crossword.friend.testnet new json-args '{"solution": "69c2feb084439956193f4c21936025f14a5a5a78979d67ae34762e18a7206a0f"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as crossword.friend.testnet network-config testnet sign-with-keychain send - ``` - - - -Now the crossword solution, as a hash, is stored instead. If you try calling the last command again, you'll get the error message, thanks to the `#[init]` macro: -`The contract has already been initialized` - -## First use of Batch Actions - -This is close to what we want, but what if a person deploys their smart contract and **someone else** quickly calls the `new` function before them? We want to make sure the same person who deployed the contract sets the solution, and we can do this using Batch Actions. Besides, why send two transactions when we can do it in one? (Technical details covered in the spec for a [batch transaction here](https://nomicon.io/RuntimeSpec/Transactions.html?highlight=batch#batched-transaction).) - -
- Cookie sheet representing a transaction, where cookies are Deploy and FunctionCall Actions. Art created by dobulyo.near. -
Art by dobulyo.near
-

- -:::info Batch Actions in use -Batch Actions are common in this instance, where we want to deploy and call an initialization function. They're also common when using a factory pattern, where a subaccount is created, a smart contract is deployed to it, a key is added, and a function is called. - -Here's a truncated snippet from a useful (though somewhat advanced) repository with a wealth of useful code: - - -We'll get into Actions later in this tutorial, but in the meantime here's a handy [reference from the spec](https://nomicon.io/RuntimeSpec/Actions.html). -::: - -As you can from the info bubble above, we can batch [Deploy](https://docs.rs/near-sdk/3.1.0/near_sdk/struct.Promise.html#method.deploy_contract) and [FunctionCall](https://docs.rs/near-sdk/3.1.0/near_sdk/struct.Promise.html#method.function_call) Actions. This is exactly what we want to do for our crossword puzzle, and luckily, NEAR CLI has a [flag especially for this](/tools/near-cli#deploy). - -Let's run this again with the handy `--initFunction` and `--initArgs` flags: - -Create fresh account if you wish, which is good practice: - - - - - ```bash - # Delete an account - near delete-account crossword.friend.testnet friend.testnet --networkId testnet - - # Create an account again - near create-account crossword.friend.testnet --use-account friend.testnet --initial-balance 1 --network-id testnet - ``` - - - - - ```bash - # Delete an account - near account delete-account crossword.friend.testnet beneficiary friend.testnet network-config testnet sign-with-keychain send - - # Create an account again - near account create-account fund-myself crossword.friend.testnet '1 NEAR' autogenerate-new-keypair save-to-keychain sign-as friend.testnet network-config testnet sign-with-keychain send - ``` - - - -Deploy the contract and call the initialization method: - -```bash -cargo near deploy build-non-reproducible-wasm crossword.friend.testnet with-init-call new json-args '{"solution": "69c2feb084439956193f4c21936025f14a5a5a78979d67ae34762e18a7206a0f"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send -``` - -Now that we're using Batch Actions, no one can call this `new` method before us. - -:::note Batch action failures -If one Action in a set of Batch Actions fails, the entire transaction is reverted. This is good to note because sharded, proof-of-stake systems do not work like proof-of-work where a complex transaction with multiple cross-contract calls reverts if one call fails. With NEAR, cross-contract calls use callbacks to ensure expected behavior, but we'll get to that later. -::: - -## Get ready for our frontend - -In the previous section we showed that we could use a `curl` command to view the state of the contract without explicitly having a function that returns a value from state. Now that we've demonstrated that and hashed the solution, let's add a short view-only function `get_solution`. - -In the next section we'll add a simple frontend for our single, hardcoded crossword puzzle. We'll want to easily call a function to get the final solution hash. We can use this opportunity to remove the function `get_puzzle_number` and the constant it returns, as these were use for informative purposes. - -We'll also modify our `guess_solution` to return a boolean value, which will also make things easier for our frontend. - - - -The `get_solution` method can be called with: - - - - - ```bash - near view crossword.friend.testnet get_solution '{}' --networkId testnet - ``` - - - - - ```bash - near contract call-function as-read-only crossword.friend.testnet get_solution json-args {} network-config testnet now - ``` - - - -In the next section we'll add a simple frontend. Following chapters will illustrate more NEAR concepts built on top of this idea. diff --git a/docs/tutorials/crosswords/02-beginner/02-structs-enums.md b/docs/tutorials/crosswords/02-beginner/02-structs-enums.md deleted file mode 100644 index 7e1ec368838..00000000000 --- a/docs/tutorials/crosswords/02-beginner/02-structs-enums.md +++ /dev/null @@ -1,216 +0,0 @@ ---- -sidebar_position: 3 -sidebar_label: Using structs and enums -title: How to think about structs and enums when writing a Rust smart contract on NEAR -description: "Learn how to use structs and enums in Rust smart contracts on NEAR to organize and manage crossword puzzle data effectively." ---- - -import basicCrossword from '/assets/docs/tutorials/crosswords/basics-crossword.jpg'; -import enumBox from '/assets/docs/tutorials/crosswords/enum-a-d-block--eizaconiendo.near--eiza_coniendo.png'; - -In this chapter, we will explore how to use structs and enums in Rust smart contracts on NEAR. Structs are used to model complex data types, while enums help represent discrete states or options. We will see how these concepts can be applied to our crossword puzzle smart contract, allowing us to store and manage puzzle data effectively. - -# Structs and enums - -## Overview - -### Structs - -If you're not familiar with Rust, it may be confusing that there are no classes or inheritance like other programming languages. We'll be exploring how to [use structs](https://doc.rust-lang.org/book/ch05-01-defining-structs.html), which are someone similar to classes, but perhaps simpler. - -Remember that there will be only one struct that gets the [`#[near]` macro](../../../smart-contracts/anatomy/anatomy.md) placed on it; our primary struct or singleton if you wish. Oftentimes the primary struct will contain additional structs that may, in turn, contain more structs in a neat and orderly way. You may also have structs that are used to return data to an end user, like a frontend. We'll be covering both of these cases in this chapter. - -### Enums - -Enums are short for enumerations, and can be particularly useful if you have entities in your smart contract that transition to different states. For example, say you have a series of blockchain games where players can join, battle, and win. There might be an enumeration for `AcceptingPlayers`, `GameInProgress`, and `GameCompleted`. Enums are also used to define discrete types of concept, like months in a year. - -For our crossword puzzle, one example of an enum is the direction of the clue: either across (A) or down (D) as illustrated below. These are the only two options. - -
- Children's toy of a box that has blocks that only fit certain shapes, resembling the letters A and D. Art created by eizaconiendo.near -
Art by eizaconiendo.near
-
-
- -Rust has an interesting feature where enums can contain additional data. You can see [examples of that here](https://doc.rust-lang.org/rust-by-example/custom_types/enum.html). - -## Using structs - -### Storing contract state - -We're going to introduce several structs all at once. These structs are addressing a need from the previous chapter, where the puzzle itself was hardcoded and looked like this: - -Basic crossword puzzle from chapter 1 - -In this chapter, we want the ability to add multiple, custom crossword puzzles. This means we'll be storing information about the clues in the contract state. Think of a grid where there are x and y coordinates for where a clue starts. We'll also want to specify: - -1. Clue number -2. Whether it's **across** or **down** -3. The length, or number of letters in the answer - -Let's dive right in, starting with our primary struct: - -```rust -#[near(contract_state)] -#[derive(PanicOnDefault)] -pub struct Crossword { - puzzles: LookupMap, // ⟵ Puzzle is a struct we're defining - unsolved_puzzles: UnorderedSet, -} -``` - -:::note Let's ignore a couple of things… -For now, let's ignore the macros about the structs that begin with `derive` and `near`. -::: - -Look at the fields inside the `Crossword` struct above, and you'll see a couple types. `String` is a part of Rust's standard library, but `Puzzle` is something we've created: - -```rust -#[near(serializers = [borsh])] -#[derive(Debug)] -pub struct Puzzle { - status: PuzzleStatus, // ⟵ An enum we'll get to soon - /// Use the CoordinatePair assuming the origin is (0, 0) in the top left side of the puzzle. - answer: Vec, // ⟵ Another struct we've defined -} -``` - -Let's focus on the `answer` field here, which is a vector of `Answer`s. (A vector is nothing fancy, just a bunch of items or a "growable array" as described in the [standard Rust documentation](https://doc.rust-lang.org/std/vec/struct.Vec.html). - -```rust -#[near(serializers = [json, borsh])] -#[derive(Debug)] -pub struct Answer { - num: u8, - start: CoordinatePair, // ⟵ Another struct we've defined - direction: AnswerDirection, // ⟵ An enum we'll get to soon - length: u8, - clue: String, -} -``` - -Now let's take a look at the last struct we'e defined, that has cascaded down from fields on our primary struct: the `CoordinatePair`. - -```rust -#[near(serializers = [json, borsh])] -#[derive(Debug)] -pub struct CoordinatePair { - x: u8, - y: u8, -} -``` - -:::info Summary of the structs shown -There are a handful of structs here, and this will be a typical pattern when we use structs to store contract state. - -``` -Crossword ⟵ primary struct with #[near(contract_state)] -└── Puzzle - └── Answer - └── CoordinatePair -``` -::: - -### Returning data - -Since we're going to have multiple crossword puzzles that have their own, unique clues and positions in a grid, we'll want to return puzzle objects to a frontend. - -:::tip Quick note on return values -By default, return values are serialized in JSON unless explicitly directed to use Borsh for binary serialization. - -For example, if we call this function: - -```rust -pub fn return_some_words() -> Vec { - vec!["crossword".to_string(), "puzzle".to_string()] -} -``` - -The return value would be a JSON array: - -`["crossword", "puzzle"]` - -While somewhat advanced, you can learn more about [changing the serialization here](../../../smart-contracts/anatomy/serialization-interface.md). -::: - -We have a struct called `JsonPuzzle` that differs from the `Puzzle` struct we've shown. It has one difference: the addition of the `solution_hash` field. - -```rust -#[near(serializers = [json])] -pub struct JsonPuzzle { - /// The human-readable (not in bytes) hash of the solution - solution_hash: String, // ⟵ this field is not contained in the Puzzle struct - status: PuzzleStatus, - answer: Vec, -} -``` - -This is handy because our primary struct has a key-value pair where the key is the solution hash (as a `String`) and the value is the `Puzzle` struct. - -```rust -pub struct Crossword { - puzzles: LookupMap, - // key ↗ ↖ value - … -``` - -Our `JsonPuzzle` struct returns the information from both the key and the value. - -We can move on from this topic, but suffice it to say, sometimes it's helpful to have structs where the intended use is to return data in a more meaningful way than might exist from the structs used to store contract data. - -### Using returned objects in a callback - -Don't be alarmed if this section feels confusing at this point, but know we'll cover Promises and callbacks later. - -Without getting into detail, a contract may want to make a cross-contract call and "do something" with the return value. Sometimes this return value is an object we're expecting, so we can define a struct with the expected fields to capture the value. In other programming languages this may be referred to as "casting" or "marshaling" the value. - -A real-world example of this might be the [Storage Management standard](https://nomicon.io/Standards/StorageManagement.html), as used in a [fungible token](https://github.com/near-examples/FT). - -Let's say a smart contract wants to determine if `alice.near` is "registered" on the `nDAI` token. More technically, does `alice.near` have a key-value pair for herself in the fungible token contract. - -```rust -#[near(serializers = [json])] -pub struct StorageBalance { - pub total: U128, - pub available: U128, -} - -// … -// Logic that calls the nDAI token contract, asking for alice.near's storage balance. -// … - -#[private] -pub fn my_callback(&mut self, #[callback] storage_balance: StorageBalance) { - // … -} -``` - -The crossword puzzle will eventually use a cross-contract call and callback, so we can look forward to that. For now just know that if your contract expects to receive a return value that's not a primitive (unsigned integer, string, etc.) and is more complex, you may use a struct to give it the proper type. - -## Using enums - -In the section above, we saw two fields in the structs that had an enum type: - -1.`AnswerDirection` — this is the simplest type of enum, and will look familiar from other programming languages. It provides the only two options for how a clue in oriented in a crossword puzzle: across and down. - -```rust -#[near(serializers = [json, borsh])] -#[derive(Debug)] -pub enum AnswerDirection { - Across, - Down, -} -``` - -2. `PuzzleStatus` — this enum can actually store a string inside the `Solved` structure. (Note that we could have simply stored a string instead of having a structure, but a structure might make this easier to read.) - -As we improve our crossword puzzle, the idea is to give the winner of the crossword puzzle (the first person to solve it) the ability to write a memo. (For example: "Took me forever to get clue six!", "Alice rules!" or whatever.) - -```rust -#[near(serializers = [json, borsh])] -#[derive(Debug)] -pub enum PuzzleStatus { - Unsolved, - Solved { memo: String }, -} -``` diff --git a/docs/tutorials/crosswords/02-beginner/03-actions.md b/docs/tutorials/crosswords/02-beginner/03-actions.md deleted file mode 100644 index dd6fbb2153f..00000000000 --- a/docs/tutorials/crosswords/02-beginner/03-actions.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -sidebar_position: 4 -sidebar_label: Actions and sending NEAR -title: There are several Actions an account can do, including sending the winner of the crossword puzzle NEAR using the Transfer Action -description: "Learn how to use the Transfer Action in NEAR smart contracts to send a prize to the first crossword puzzle solver." ---- -import {Github} from "@site/src/components/UI/Codetabs" - -import allActions from '/assets/docs/tutorials/crosswords/crossword-actions.png'; -import transferNEAR from '/assets/docs/tutorials/crosswords/transfer-brand-blue--qiqi04.near--blankworl.png'; -import yoctoNEAR from '/assets/docs/tutorials/crosswords/yoctoNEAR-magnifying--jrbemint.near--JrbeMad.jpg'; -import signerPredecessorCurrent from '/assets/docs/tutorials/crosswords/predecessor-signer-current--yasuoarts.near--YasuoArt69.png'; - - -We're going to introduce a new Action: `Transfer`. In this chapter, we'd like the first person to solve the crossword puzzle to earn some prize money, sent in NEAR. - -# Actions (including sending NEAR) - -
- Two hands exchanging a coin emblazoned with the NEAR Protocol logo. Art created by qiqi04.near -
Art by qiqi04.near
-
-
- -We've already used Actions in the [previous chapter](/tutorials/crosswords/basics/hashing-and-unit-tests#first-use-of-batch-actions), when we deployed and initialized the contract, which used the `DeployContract` and `FunctionCall` Action, respectively. - -The full list of Actions are available at the [NEAR specification site](https://nomicon.io/RuntimeSpec/Actions.html). - -By the end of this entire tutorial we'll have used all the Actions highlighted below: - -All Actions that will be used when the entire crossword puzzle tutorial is complete - -## Actions from within a contract - -When we deployed and initialized the contract, we used NEAR CLI in our Terminal or Command Prompt app. At a high level, this might feel like we're lobbing a transaction into the blockchain, instructing it to do a couple actions. - -It's important to note that you can also execute Actions inside a smart contract, which is what we'll be doing. In the sidebar on the left, you'll see a section called [**Promises**](../../../smart-contracts/anatomy/actions.md), which provides examples of this. Perhaps it's worth mentioning that for the Rust SDK, Promises and Actions are somewhat synonymous. - -:::note Actions only effect the current contract -A contract cannot use the `AddKey` Action on another account, including the account that just called it. It can only add a key to *itself*, if that makes sense. - -The same idea applies for the other actions as well. You cannot deploy a contract to someone else's account, or delete a different account. (Thankfully 😅) - -Similarly, when we use the `Transfer` Action to send the crossword puzzle winner their prize, the amount is being subtracted from the account balance of the account where the crossword contract is deployed. - -The only interesting wrinkle (and what may *seem like* an exception) is when a subaccount is created using the `CreateAccount` Action. During that transaction, you may use Batch Actions to do several things like deploy a contract, transfer NEAR, add a key, call a function, etc. This is common in smart contracts that use a factory pattern, and we'll get to this in future chapters of this tutorial. -::: - -## Define the prize amount - -Let's make it simple and hardcode the prize amount. This is how much NEAR will be given to the first person who solves the crossword puzzle, and will apply to all the crossword puzzles we add. We'll make this amount adjustable in future chapters. - -At the top of the `lib.rs` file we'll add this constant: - - - -As the code comment mentions, this is 5 NEAR, but look at all those zeroes in the code! - -That's the value in yoctoNEAR. This concept is similar to other blockchains. Bitcoin's smallest unit is a satoshi and Ethereum's is a wei. - -
- Depiction of bills of NEAR, coins for partial NEAR, and then a magnifying glass showing a tiny yoctoNEAR next to an ant. Art created by jrbemint.near -
Art by jrbemint.near
-
- -## Adding `Transfer` - -In the last chapter we had a simple function called `guess_solution` that returned `true` if the solution was correct, and `false` otherwise. We'll be replacing that function with `submit_solution` as shown below: - - - -Note the last line in this function, which sends NEAR to the predecessor. - -:::info Returning a Promise -The last line of the function above ends with a semicolon. If the semicolon were removed, that would tell Rust that we'd like to return this Promise object. - -It would be perfectly fine to write the function like this: - -```rust -pub fn submit_solution(&mut self, solution: String, memo: String) -> Promise { - // … - // Transfer the prize money to the winner - Promise::new(env::predecessor_account_id()).transfer(PRIZE_AMOUNT) -} -``` -::: - -## Predecessor, signer, and current account - -When writing a smart contract you'll commonly want to use `env` and the details it provides. We used this in the last chapter for: - -- logging (ex: `env::log_str("hello friend")`) -- hashing using sha256 (ex: `env::sha256(solution.as_bytes())`) - -There are more functions detailed in the [SDK reference docs](https://docs.rs/near-sdk/latest/near_sdk/env/index.html). - -Let's cover three commonly-used functions regarding accounts: predecessor, signer, and current account. - -
- Illustration of Alice sending a transaction to a smart contract named Banana, which does a cross-contract call to the smart contract Cucumber. Art created by yasuoarts.near -
Alice sends a transaction to the contract on banana.near, which does a cross-contract call to cucumber.near.
From the perspective of a contract on cucumber.near, we see a list of the predecessor, signer, and current account.
Art by yasuoarts.near
-


- -1. [predecessor account](https://docs.rs/near-sdk/latest/near_sdk/env/fn.predecessor_account_id.html) — `env::predecessor_account_id()` - - This is the account that was the immediate caller to the smart contract. If this is a simple transaction (no cross-contract calls) from **alice.near** to **banana.near**, the smart contract at **banana.near** considers Alice the predecessor. In this case, Alice would *also* be the signer. - - :::tip When in doubt, use predecessor - As we explore the differences between predecessor and signer, know that it's a more common **best practice to choose the predecessor**. - - Using the predecessor guards against a potentially malicious contract trying to "fool" another contract that only checks the signer. - ::: - -2. [signer account](https://docs.rs/near-sdk/latest/near_sdk/env/fn.signer_account_id.html) — `env::signer_account_id()` - - The signer is the account that originally *signed* the transaction that began the blockchain activity, which may or may not include cross-contract calls. If a function calls results in several cross-contract calls, think of the signer as the account that pushed over the first domino in that chain reaction. - - :::caution Beware of middlemen - If your smart contract is checking the ownership over some assets (fungible token, NFTs, etc.) it's probably a bad idea to use the signer account. - - A confused or malicious contract might act as a middleman and cause unexpected behavior. If **alice.near** accidentally calls **evil.near**, the contract at that account might do a cross-contract call to **vulnerable-nft.near**, instructing it to transfer an NFT. - - If **vulnerable-nft.near** only checks the signer account to determine ownership of the NFT, it might unwittingly give away Alice's property. Checking the predecessor account eliminates this problem. - ::: - -3. [current account](https://docs.rs/near-sdk/latest/near_sdk/env/fn.current_account_id.html) — `env::current_account_id()` - - The current account is "me" from the perspective of a smart contract. - - :::tip Why would I use that? - There might be various reasons to use the current account, but a common use case is checking ownership or handling callbacks to cross-contract calls. - - Many smart contracts will want to implement some sort of permission system. A common, rudimentary permission allows certain functions to only be called by the contract owner, AKA the person who owns a private key to the account for this contract. - - The contract can check that the predecessor and current account are the same, and trust offer more permissions like changing contract settings, upgrading the contract, or other privileged modifications. - ::: diff --git a/docs/tutorials/crosswords/03-intermediate/02-use-seed-phrase.md b/docs/tutorials/crosswords/03-intermediate/02-use-seed-phrase.md deleted file mode 100644 index b367b81e9d5..00000000000 --- a/docs/tutorials/crosswords/03-intermediate/02-use-seed-phrase.md +++ /dev/null @@ -1,85 +0,0 @@ ---- -sidebar_position: 3 -sidebar_label: Seed phrase logic -title: Implementing the seed phrase logic from the necessary libraries -description: "Learn how to use seed phrases in NEAR to generate new accounts and securely derive keys from puzzle solutions." ---- - -In this chapter, we will implement the seed phrase logic necessary for the crossword puzzle. This includes generating a random seed phrase for new accounts and parsing the solution as a seed phrase to derive keys for submitting solutions. We will use the `near-seed-phrase` library to handle these tasks. - -# Seed phrase and key derivation - -There are two separate things we'll want to do: - -1. **Create a random seed phrase** for the user when they visit the crossword puzzle. This will be used if they win and don't have a NEAR account and wish to create one. They can then paste this seed phrase into NEAR Wallet afterward to import their account (which is basically like "logging in" and is currently possible at https://testnet.mynearwallet.com/recover-seed-phrase). -2. **Turn the crossword solution into a key pair**, instead of just hashing it. - -## near-seed-phrase library - -We can add the `near-seed-phrase` package to our project with: - -```bash -npm install near-seed-phrase --save -``` - -:::note Code snippets for this chapter -At this point in the tutorial, it's more difficult to share code snippets that are both meaningful and meant to be copy/pasted into a project. - -The snippets provided might differ slightly from the implementation of the [completed code for chapter 3](https://github.com/near-examples/crossword-tutorial-chapter-3), which might be the best place to look for the functioning code. -::: - -## Generate random seed phrase for new account creation (if the winner doesn't already have an account) - -```js -import { generateSeedPhrase } from 'near-seed-phrase'; - -// Create a random key in here -let seedPhrase = generateSeedPhrase(); // generateSeedPhrase() returns an object {seedPhrase, publicKey, secretKey} -localStorage.setItem('playerKeyPair', JSON.stringify(seedPhrase)); -``` - -## Parse solution as seed phrase - -(This security measure prevents front-running.) - -```js -import { parseSeedPhrase } from 'near-seed-phrase'; -// Get the seed phrase from the completed puzzle. -// The original puzzle creator would have already called this same function with the same inputs and would have -// already called `AddKey` on this contract to add the key related to this seed phrase. Here, using this deterministic -// function, the front-end will automatically generate that same key based on the inputs from the winner. -const seedPhrase = parseSolutionSeedPhrase(data, gridData); // returns a string of space-separated words -// Get the public and private key derived from the seed phrase -const {secretKey, publicKey} = parseSeedPhrase(seedPhrase); - -// Set up the account and connection, acting on behalf of the crossword account -const keyStore = new nearAPI.keyStores.InMemoryKeyStore(); // Another type of key -const keyPair = nearAPI.utils.key_pair.KeyPair.fromString(secretKey); -await keyStore.setKey(nearConfig.networkId, nearConfig.contractName, keyPair); -nearConfig.keyStore = keyStore; -const near = await nearAPI.connect(nearConfig); -const crosswordAccount = await near.account(nearConfig.contractName); - -// Call the submit_solution method using the discovered function-call access key -let transaction = await crosswordAccount.functionCall(…); -``` - -The last line should look familiar. We did something similar in the last chapter, except we used the `WalletConnection`'s account to do the function call. - -This time we're using an `InMemoryKeyStore` instead of the browser, as you can see toward the middle of the snippet. - -### Key stores - -We have now used almost all the key stores available in `near-api-js`: - -1. `UnencryptedFileSystemKeyStore` — early on, when we used the NEAR CLI command `near login`, this created a file in our operating system's home directory containing a private, full-access key to our account. -2. `BrowserLocalStorageKeyStore` — in the last chapter, when the user first logs in, the function-call access key is saved in the browser's local storage. -3. `InMemoryKeyStore` — for this chapter, we'll simply use the computer's memory to store the private key derived from the crossword solution. - -:::tip You can have multiple key stores -Technically, there's another type of key store called the `MergeKeyStore`. - -Say you want to look for private keys in various directories. You can essentially have a list of `UnencryptedFileSystemKeyStore` key stores that look in different places. - -Use the `MergeKeyStore` when you might want to look for a private key in more than one place. -::: diff --git a/docs/tutorials/crosswords/03-intermediate/03-linkdrop.md b/docs/tutorials/crosswords/03-intermediate/03-linkdrop.md deleted file mode 100644 index d7036921540..00000000000 --- a/docs/tutorials/crosswords/03-intermediate/03-linkdrop.md +++ /dev/null @@ -1,131 +0,0 @@ ---- -sidebar_position: 4 -sidebar_label: Linkdrop contract -title: Introducing the linkdrop contract we can use -description: "Learn how the linkdrop contract enables creating new NEAR accounts through cross-contract calls." ---- -import {Github} from "@site/src/components/UI/Codetabs"; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -import createMainnetAccount from '/assets/docs/tutorials/crosswords/create-mainnet-account.png'; -import createTestnetAccount from '/assets/docs/tutorials/crosswords/create-testnet-wallet-account.png'; - -We're going to take a small detour to talk about the linkdrop smart contract. It's best that we first understand this contract and its purpose, then discuss calling a method on this contract. - -# The linkdrop contract - -[The linkdrop contract](https://github.com/near/near-linkdrop) is deployed to the accounts `testnet` and `near`, which are known as the top-level accounts of the testnet and mainnet network, respectively. (Anyone can create a linkdrop-style contract elsewhere, but the one shown here is the main one that others are patterned off of.) - -## Testnet - -There’s nothing special about testnet accounts; there is no real-world cost to you as a developer when creating testnet accounts, so feel free to create or delete at your convenience. - -When a user signs up for a testnet account on NEAR Wallet, they'll see this: - - - -Let's discuss how this testnet account gets created. - -Notice the new account will end in `.testnet`. This is because the account `testnet` will create a subaccount (like we learned about [earlier in this tutorial](../01-basics/02-add-functions-call.md#create-a-subaccount)) called `vacant-name.testnet`. - -There are two ways to create this subaccount: - -1. Use a full-access key for the account `testnet` to sign a transaction with the `CreateAccount` Action. -2. In a smart contract deployed to the `testnet` account, call the `CreateAccount` Action, which is an async method that returns a Promise. (More info about writing a [`CreateAccount` Promise](../../../smart-contracts/anatomy/actions.md#create-a-sub-account)) - -(In the example below that uses NEAR CLI to create a new account, it's calling `CreateAccount` on the linkdrop contract that is deployed to the top level "near" account on mainnet.) - - -## Mainnet - -On mainnet, the account `near` also has the linkdrop contract deployed to it. - -Using NEAR CLI, a person can create a `mainnet` account by calling the linkdrop contract, like shown below: - - - - - ```bash - near call near create_account '{"new_account_id": "aloha.near", "new_public_key": "3cQ...tAT"}' --gas 300000000000000 --deposit 15 --accountId mike.near --networkId mainnet - ``` - - - - - ```bash - near contract call-function as-transaction near create_account json-args '{"new_account_id": "aloha.near", "new_public_key": "3cQ...tAT"}' prepaid-gas '300.0 Tgas' attached-deposit '15 NEAR' sign-as mike.near network-config mainnet sign-with-keychain - ``` - - - -The above command calls the `create_account` method on the account `near`, and would create `aloha.near` **if it's available**, funding it with 15 Ⓝ. - -We'll want to write a smart contract that calls that same method. However, things get interesting because it's possible `aloha.near` is already taken, so we'll need to learn how to handle that. - -## A simple callback - -### The `create_account` method - -Here, we'll show the implementation of the `create_account` method. Note the `#[payable]` macro, which allows this function to accept an attached deposit. (Remember in the CLI command we were attaching 15 Ⓝ.) - - - -The most important part of the snippet above is around the middle where there's: - -```rust -Promise::new(...) - ... - .then( - Self::ext(env::current_account_id()) - .on_account_created(...) - ) -``` - -This translates to, "we're going to attempt to perform an Action, and when we're done, please call myself at the method `on_account_created` so we can see how that went." - -:::caution This doesn't work - -Not infrequently, developers will attempt to do this in a smart contract: - -```rust -let creation_result = Promise::new("aloha.mike.near") - .create_account(); - -// Check creation_result variable (can't do it!) -if creation_result {...} - -``` - -In other programming languages promises might work like this, but we must use callbacks instead. -::: - -### The callback - -Now let's look at the callback: - - - -This calls the private helper method `is_promise_success`, which basically checks to see that there was only one promise result, because we only attempted one Promise: - - - -Note that the callback returns a boolean. This means when we modify our crossword puzzle to call the linkdrop contract on `testnet`, we'll be able to determine if the account creation succeeded or failed. - -And that's it! Now we've seen a method and a callback in action for a simple contract. - -:::tip This is important -Understanding cross-contract calls and callbacks is quite important in smart contract development. - -Since NEAR's transactions are asynchronous, the use of callbacks may be a new paradigm shift for smart contract developers from other ecosystems. - -Feel free to dig into the linkdrop contract and play with the ideas presented in this section. - -There are two additional examples that are helpful to look at: -1. [High-level cross-contract calls](https://github.com/near/near-sdk-rs/blob/master/examples/cross-contract-calls/high-level/src/lib.rs) — this is similar what we've seen in the linkdrop contract. -2. [Low-level cross-contract calls](https://github.com/near/near-sdk-rs/blob/master/examples/cross-contract-calls/low-level/src/lib.rs) — a different approach where you don't use the traits we mentioned. -::: - ---- - -Next we'll modify the crossword puzzle contract to check for the signer's public key, which is how we now determine if they solved the puzzle correctly. diff --git a/docs/tutorials/examples/coin-flip.md b/docs/tutorials/examples/coin-flip.md deleted file mode 100644 index 47db44fe429..00000000000 --- a/docs/tutorials/examples/coin-flip.md +++ /dev/null @@ -1,135 +0,0 @@ ---- -id: coin-flip -title: Coin Flip -description: "Learn to handle randomness on NEAR." ---- -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import {CodeTabs, Language, Github} from "@site/src/components/UI/Codetabs" - -This example demonstrates a simple coin flip game on the NEAR blockchain, where players can guess the outcome of a coin flip and earn points. It includes both the smart contract and the frontend components. - -![img](/assets/docs/tutorials/examples/coin-flip.png) - ---- - -## Starting the Game -Coin Flip is a game where the player tries to guess the outcome of a coin flip. It is one of the simplest contracts implementing random numbers. - -You have two options to start the example: -1. **Recommended:** use the app through Gitpod (a web-based interactive environment) -2. Clone the project locally. - -| Gitpod | Clone locally | -| ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | -| Open in Gitpod | `https://github.com/near-examples/coin-flip-examples.git` | - - -If you choose Gitpod, a new browser window will open automatically with the code. Give it a minute, and the front-end will pop up (ensure the pop-up window is not blocked). - -If you are running the app locally, you should build and deploy a contract (JavaScript or Rust version) and a client manually. - ---- - -## Interacting With the Counter -Go ahead and log in with your NEAR account. If you don't have one, you can create one on the fly. Once logged in, use the `tails` and `heads` buttons to try to guess the next coin flip outcome. - -![img](/assets/docs/tutorials/examples/coin-flip.png) -*Frontend of the Game* - ---- - -## Structure of a dApp - -Now that you understand what the dApp does, let us take a closer look to its structure: - -1. The frontend code lives in the `/frontend` folder. -2. The smart contract code in Rust is in the `/contract-rs` folder. -3. The smart contract code in JavaScript is in the `/contract-ts` folder. - -:::note -Both Rust and JavaScript versions of the contract implement the same functionality. -::: - -### Contract -The contract presents 2 methods: `flip_coin`, and `points_of`. - - - - - - - - - - -### Running the Frontend - -To start the frontend you will need to install the dependencies and start the server. - -```bash -cd frontend -yarn -yarn dev -``` - -
- -### Understanding the Frontend - -The frontend is a [Next.JS](https://nextjs.org/) project generated by [create-near-app](https://github.com/near/create-near-app). Check `_app.js` and `index.js` to understand how components are displayed and interacting with the contract. - - - - - - ---- - -## Testing - -When writing smart contracts, it is very important to test all methods exhaustively. In this -project you have integration tests. Before digging into them, go ahead and perform the tests present in the dApp through the command `yarn test` for the JavaScript version, or `./test.sh` for the Rust version. - -### Integration test - -Integration tests can be written in both Rust and JavaScript. They automatically deploy a new -contract and execute methods on it. In this way, integration tests simulate interactions -from users in a realistic scenario. You will find the integration tests for the `coin-flip` -in `contract-ts/sandbox-ts` (for the JavaScript contract) and `contract-rs/tests` (for the Rust contract). - - - - - - - - - - ---- - -## A Note On Randomness - -Randomness in the blockchain is a complex subject. We recommend you to read and investigate about it. -You can start with our [security page on it](../../smart-contracts/security/random.md). - -:::note Versioning for this article - -At the time of this writing, this example works with the following versions: - -- near-cli: `4.0.13` -- node: `18.19.1` -- rustc: `1.77.0` - -::: diff --git a/docs/tutorials/examples/count-near.md b/docs/tutorials/examples/count-near.md deleted file mode 100644 index 872591fc518..00000000000 --- a/docs/tutorials/examples/count-near.md +++ /dev/null @@ -1,360 +0,0 @@ ---- -id: count-near -title: Count on NEAR -description: "A simple counter on NEAR Protocol." ---- - -import {CodeTabs, Language, Github} from '@site/src/components/UI/Codetabs'; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import MovingForwardSupportSection from '@site/src/components/MovingForwardSupportSection'; - -Our counter example is a friendly decentralized app that stores a number and exposes methods to `increment`,`decrement`, and `reset` it. - -![img](/assets/docs/tutorials/examples/count-on-near-banner.png) - ---- - -## Obtaining the Counter Example - -You have two options to start the Counter Example. - -1. You can use the app through `GitHub Codespaces`, which will open a web-based interactive environment. -2. Clone the repository locally and use it from your computer. - -| Codespaces | Clone locally | -| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------- | -| [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/near-examples/counters) | 🌐 `https://github.com/near-examples/counters` | - ---- - -## Structure of the Example - -The example is divided in two main components: - -1. The smart contract, available in three flavors: Rust, JavaScript, and Python -2. The frontend, that interacts with an already deployed contract. - - - - - -```bash -┌── sandbox-ts # sandbox testing -│ ├── src -│ │ └── main.ava.ts -│ ├── ava.config.cjs -│ └── package.json -├── src # contract's code -│ └── contract.ts -├── package.json # package manager -├── README.md -└── tsconfig.json # test script -``` - - - - - -```bash -┌── src # contract's code -│ └── lib.rs -├── tests # sandbox test -│ └── test_basics.rs -├── Cargo.toml # package manager -├── README.md -└── rust-toolchain.toml -``` - - - - - -```bash -├── tests # contract tests -│ └── test_contract.py -├── contract.py # contract's code -├── contract.wasm # compiled contract -├── pyproject.toml # package manager -├── README.md -└── uv.lock -``` - - - - - ---- - -## Frontend - -The counter example includes a frontend interface designed to interact seamlessly with an existing smart contract that has been deployed. This interface allows users to increase or decrease the counter as needed. - -
- -### Running the Frontend - -To start the frontend you will need to install the dependencies and start the server. - -```bash -cd frontend -yarn -yarn dev -``` - -Go ahead and login with your NEAR account. If you don't have one, you will be able to create one in the moment. Once logged in, use the `+` and `-` buttons to increase and decrease the counter. Then, use the Gameboy buttons to reset it and make the counter blink an eye! - -![img](/assets/docs/tutorials/examples/count-on-near.png) -_Frontend of the Counter_ - -
- -### Understanding the Frontend - -The frontend is a [Next.JS](https://nextjs.org/) project generated by [create-near-app](https://github.com/near/create-near-app). Check `_app.js` and `index.js` to understand how components are displayed and interacting with the contract. - - - - - - ---- - -## Smart Contract - -The contract presents 4 methods: `get_num`, `increment`, `decrement`, and `reset`. The method `get_num` retrieves the current value, and the rest modify it. - - - - - - - - - - - - - ---- - -### Testing the Contract - -The contract readily includes a set of unit and sandbox testing to validate its functionality. To execute the tests, run the following commands: - - - - -```bash -cd contract-ts -yarn -yarn test -``` - - - - - ```bash - cd contract-rs - cargo test - ``` - - - - - - ```bash - cd contract-py - uv run pytest - ``` - - - - - -:::tip -The `integration tests` use a sandbox to create NEAR users and simulate interactions with the contract. -::: - -
- -### Deploying the Contract to the NEAR network - -In order to deploy the contract you will need to create a NEAR account. - - - - - ```bash - # Create a new account pre-funded by a faucet - near create-account --useFaucet - ``` - - - - - ```bash - # Create a new account pre-funded by a faucet - near account create-account sponsor-by-faucet-service .testnet autogenerate-new-keypair save-to-keychain network-config testnet create - ``` - - - -Go into the directory containing the smart contract (`cd contract-ts`, `cd contract-rs`, or `cd contract-py`), build and deploy it: - - - - - - ```bash - npm run build - near deploy ./build/counter.wasm - ``` - - - - - ```bash - cargo near deploy build-non-reproducible-wasm - ``` - - - - - - ```bash - uvx nearc contract.py - near deploy contract.wasm - near call initialize '{}' --accountId - ``` - - - - - -:::tip -To interact with your contract from the [frontend](#frontend), simply replace the value of the `testnet` key in the `config.js` file. -::: - - -
- -### CLI: Interacting with the Contract - -To interact with the contract through the console, you can use the following commands. - -#### Get the current number of the counter - - - - - ```bash - near view counter.near-examples.testnet get_num - ``` - - - - - ```bash - near contract call-function as-read-only counter.near-examples.testnet get_num json-args {} network-config testnet now - ``` - - - -
- -#### Increment the counter - - - - - ```bash - # Replace with your account ID - near call counter.near-examples.testnet increment --accountId - ``` - - - - - ```bash - # Replace with your account ID - near contract call-function as-transaction counter.near-examples.testnet increment json-args {} prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' sign-as aha_6.testnet network-config testnet sign-with-keychain send - ``` - - - -
- -#### Decrement the counter - - - - - ```bash - # Replace with your account ID - near call counter.near-examples.testnet decrement --accountId - ``` - - - - - ```bash - # Replace with your account ID - near contract call-function as-transaction counter.near-examples.testnet decrement json-args {} prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' sign-as aha_6.testnet network-config testnet sign-with-keychain send - ``` - - - -
- -#### Reset the counter to zero - - - - - ```bash - # Replace with your account ID - near call counter.near-examples.testnet reset --accountId - ``` - - - - - ```bash - # Replace with your account ID - near contract call-function as-transaction counter.near-examples.testnet reset json-args {} prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' sign-as aha_6.testnet network-config testnet sign-with-keychain send - ``` - - - -:::tip -If you're using your own account, replace `counter.near-examples.testnet` with your `accountId`. -::: - ---- - -## Moving Forward - -A nice way to learn is by trying to expand the contract. Modify it by adding a parameter to `increment` and `decrement`, -so the user can choose by how much to change the value. For this, you will need to use knowledge from the [anatomy](../../smart-contracts/anatomy/anatomy.md) -and [storage](../../smart-contracts/anatomy/storage.md) sections. - - - -:::note Versioning for this article - -At the time of this writing, this example works with the following versions: - -- near-cli: `4.0.13` -- node: `18.19.1` -- rustc: `1.77.0` - -::: diff --git a/docs/tutorials/examples/donation.md b/docs/tutorials/examples/donation.md deleted file mode 100644 index 26c89711aef..00000000000 --- a/docs/tutorials/examples/donation.md +++ /dev/null @@ -1,346 +0,0 @@ ---- -id: donation -title: Donation -description: "Learn to build a donation smart contract that accepts NEAR tokens, tracks donations, and distributes funds to beneficiaries." ---- - -import {CodeTabs, Language, Github} from '@site/src/components/UI/Codetabs'; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import MovingForwardSupportSection from '@site/src/components/MovingForwardSupportSection'; - -Our donation example enables to forward NEAR Tokens to an account while keeping track of it. It is one of the simplest examples on making a contract handle transfers. - -![img](/assets/docs/tutorials/examples/donation.png) -_Frontend of the Donation App_ - ---- - -## Obtaining the Donation Example - -You have two options to start the Donation Example. - -1. You can use the app through `Github Codespaces`, which will open a web-based interactive environment. -2. Clone the repository locally and use it from your computer. - -| Codespaces | Clone locally | -| ------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------- | -| [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/near-examples/donation-examples) | 🌐 `https://github.com/near-examples/donation-examples.git` | - ---- - -## Structure of the Example - -The example is divided in two main components: - -1. The smart contract, available in two flavors: rust and javascript -2. The frontend, that interacts with an already deployed contract. - - - - - -```bash -┌── sandbox-ts # sandbox testing -│ ├── src -│ │ └── main.ava.ts -│ ├── ava.config.cjs -│ └── package.json -├── src # contract's code -│ ├── contract.ts -│ ├── model.ts -│ └── utils.ts -├── package.json # package manager -├── README.md -└── tsconfig.json # test script -``` - - - - - -```bash -┌── tests # workspaces testing -│ ├── workspaces.rs -├── src # contract's code -│ ├── donation.rs -│ └── lib.rs -├── Cargo.toml # package manager -├── README.md -└── rust-toolchain.toml -``` - - - - - ---- - -## Frontend - -The donation example includes a frontend that interacts with an already deployed smart contract, allowing user to donate NEAR tokens to a faucet service. - -
- -### Running the Frontend - -To start the frontend you will need to install the dependencies and start the server. - -```bash -cd frontend -yarn -yarn dev -``` - -Go ahead and login with your NEAR account. If you don't have one, you will be able to create one in the moment. Once logged in, input the amount of NEAR you want to donate and press the donate button. You will be redirected to the NEAR Wallet to confirm the transaction. After confirming it, the donation will be listed in the "Latest Donations". - -
- -### Understanding the Frontend - -The frontend is a [Next.JS](https://nextjs.org/) project generated by [create-near-app](https://github.com/near/create-near-app). Check `DonationsTable.jsx` and `DonationsForm.jsx` to understand how components are displayed and interacting with the contract. - - - - - - -An interesting aspect of the donation example is that it showcases how to retrieve a result after being redirected to the -NEAR wallet to accept a transaction. - ---- - -## Smart Contract - -The contract exposes methods to donate tokens (`donate`), and methods to retrieve the recorded donations (e.g. `get_donation_for_account`). - - - - - - - - - - -
- -### Testing the Contract - -The contract readily includes a set of unit and sandbox testing to validate its functionality. To execute the tests, run the following commands: - - - - - ```bash - cd contract-ts - yarn - yarn test - ``` - - - - - ```bash - cd contract-rs - cargo test - ``` - - - - - -:::tip -The `integration tests` use a sandbox to create NEAR users and simulate interactions with the contract. -::: - -
- -### Deploying the Contract to the NEAR network - -In order to deploy the contract you will need to create a NEAR account. - - - - - ```bash - # Create a new account pre-funded by a faucet - near create-account --useFaucet - ``` - - - - - ```bash - # Create a new account pre-funded by a faucet - near account create-account sponsor-by-faucet-service .testnet autogenerate-new-keypair save-to-keychain network-config testnet create - ``` - - - -Go into the directory containing the smart contract (`cd contract-ts` or `cd contract-rs`), build and deploy it: - - - - - - ```bash - npm run build - near deploy ./build/donation.wasm - ``` - - - - - ```bash - cargo near deploy build-non-reproducible-wasm - ``` - - - - - -:::tip -To interact with your contract from the [frontend](#frontend), simply replace the variable `CONTRACT_NAME` in the `index.js` file. -::: - -
- -### CLI: Interacting with the Contract - -To interact with the contract through the console, you can use the following commands - -#### Get donations - - - - - ```bash - near view donation.near-examples.testnet get_donations '{"from_index": "0","limit": "10"}' - ``` - - - - - ```bash - near contract call-function as-read-only donation.near-examples.testnet get_donations json-args '{"from_index": "0","limit": "10"}' network-config testnet now - ``` - - - -
- -#### Get beneficiary - - - - - ```bash - near view donation.near-examples.testnet get_beneficiary - ``` - - - - - ```bash - near contract call-function as-read-only donation.near-examples.testnet get_beneficiary json-args {} network-config testnet now - ``` - - - -
- -#### Get number of donors - - - - - ```bash - near view donation.near-examples.testnet number_of_donors - ``` - - - - - ```bash - near contract call-function as-read-only donation.near-examples.testnet number_of_donors json-args {} network-config testnet now - ``` - - - -
- -#### Get donation for an account - - - - - ```bash - # Require accountId - near view donation.near-examples.testnet get_donation_for_account '{"account_id":}' - ``` - - - - - ```bash - # Require accountId - near contract call-function as-read-only donation.near-examples.testnet get_donation_for_account json-args '{"account_id":}' network-config testnet now - ``` - - - -
- -#### Donate to the contract - - - - - ```bash - # Replace with your account ID - # Require deposit - near call donation.near-examples.testnet donate --accountId --deposit 0.1 - ``` - - - - - ```bash - # Replace with your account ID - # Require deposit - near contract call-function as-transaction donation.near-examples.testnet donate json-args {} prepaid-gas '30.0 Tgas' attached-deposit '0.1 NEAR' sign-as network-config testnet sign-with-keychain send - ``` - - - -
- -:::tip -If you're using your own account, replace `donation.near-examples.testnet` with your `accountId`. -::: - ---- - -## Moving Forward - -A nice way to learn is by trying to expand a contract. Modify the donation example so it accumulates the tokens in the contract -instead of sending it immediately. Then, make a method that only the `beneficiary` can call to retrieve the tokens. - - - -:::note Versioning for this article - -At the time of this writing, this example works with the following versions: - -- near-cli: `4.0.13` -- node: `18.19.1` -- rustc: `1.77.0` - -::: diff --git a/docs/tutorials/examples/guest-book.md b/docs/tutorials/examples/guest-book.md deleted file mode 100644 index 1280a797502..00000000000 --- a/docs/tutorials/examples/guest-book.md +++ /dev/null @@ -1,305 +0,0 @@ ---- -id: guest-book -title: Guest Book -description: "Create a NEAR Guest Book smart contract to store user messages, attach NEAR tokens, and integrate with a frontend, including premium messages and testing instructions" ---- - -import {CodeTabs, Language, Github} from '@site/src/components/UI/Codetabs'; -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; -import MovingForwardSupportSection from '@site/src/components/MovingForwardSupportSection'; - -This example demonstrates how to create a simple guest book application on the NEAR blockchain, allowing users to sign messages and optionally attach a small amount of NEAR as a tip. It includes both the smart contract and the frontend components. - -Our Guest Book example is a simple app composed by two main components: - -1. A smart contract that stores messages from users, allowing to attach money to them. -2. A simple web-based frontend that displays the last 10 messages posted. - -![img](/assets/docs/tutorials/examples/guest-book.png) - ---- - -## Obtaining the Guest book Example - -You have two options to start the Guest book Example. - -1. You can use the app through `GitHub Codespaces`, which will open a web-based interactive environment. -2. Clone the repository locally and use it from your computer. - -| Codespaces | Clone locally | -|-----------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------| -| [![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/near-examples/guest-book-examples) | 🌐 `https://github.com/near-examples/guest-book-examples` | - ---- - -## Structure of the Example - -The example is divided in two main components: - -1. The smart contract, available in two flavors: Rust and JavaScript -2. The frontend, that interacts with an already deployed contract. - - - - - -```bash -┌── sandbox-ts # sandbox testing -│ ├── src -│ │ └── main.ava.ts -│ ├── ava.config.cjs -│ └── package.json -├── src # contract's code -│ ├── contract.ts -│ └── model.ts -├── package.json # package manager -├── README.md -└── tsconfig.json # test script -``` - - - - - -```bash -┌── tests # workspaces testing -│ ├── workspaces.rs -├── src # contract's code -│ └── lib.rs -├── Cargo.toml # package manager -├── README.md -└── rust-toolchain.toml -``` - - - - - ---- - -## Frontend - -The guest book example includes a frontend that interacts with an already deployed smart contract, allowing user to sign a message. - -
- -### Running the Frontend - -To start the frontend you will need to install the dependencies and start the server. - -```bash -cd frontend -yarn -yarn dev -``` - -Go ahead and login with your NEAR account. If you don't have one, you will be able to create one in the moment. Once logged in, you will be able to sign a message in the guest book. You can further send some money alongside your message. If you attach more than 0.01Ⓝ then your message will be marked as "premium". - -
- -### Understanding the Frontend - -The frontend is a [Next.JS](https://nextjs.org/) project generated by [create-near-app](https://github.com/near/create-near-app). Check `_app.js` and `index.js` to understand how components are displayed and interacting with the contract. - - - - - - ---- - -## Smart Contract - -The contract presents 3 methods: `add_message`, `get_message` and `total_messages`. - - - - - - - - - - - -
- -### Testing the Contract - -The contract readily includes a set of unit and sandbox testing to validate its functionality. To execute the tests, run the following commands: - - - - -```bash -cd contract-ts -yarn -yarn test -``` - - - - - ```bash - cd contract-rs - cargo test - ``` - - - - - -:::tip -The `integration tests` use a sandbox to create NEAR users and simulate interactions with the contract. -::: - -
- -### Deploying the Contract to the NEAR network - -In order to deploy the contract you will need to create a NEAR account. - - - - - ```bash - # Create a new account pre-funded by a faucet - near create-account --useFaucet - ``` - - - - - ```bash - # Create a new account pre-funded by a faucet - near account create-account sponsor-by-faucet-service .testnet autogenerate-new-keypair save-to-keychain network-config testnet create - ``` - - - -Go into the directory containing the smart contract (`cd contract-ts` or `cd contract-rs`), build and deploy it: - - - - - - ```bash - npm run build - near deploy ./build/guest_book.wasm - ``` - - - - - ```bash - cargo near deploy build-non-reproducible-wasm - ``` - - - - - -:::tip -To interact with your contract from the [frontend](#frontend), simply replace the variable `CONTRACT_NAME` in the `index.js` file. -::: - -
- -### CLI: Interacting with the Contract - -To interact with the contract through the console, you can use the following commands. - -#### Get messages - - - - - ```bash - near view guestbook.near-examples.testnet get_messages '{"from_index": "0","limit": "10"}' - ``` - - - - - ```bash - near contract call-function as-read-only guestbook.near-examples.testnet get_messages json-args '{"from_index": "0","limit": "10"}' network-config testnet now - ``` - - - -
- -#### Get total number of messages - - - - - ```bash - near view guestbook.near-examples.testnet total_messages - ``` - - - - - ```bash - near contract call-function as-read-only guestbook.near-examples.testnet total_messages json-args {} network-config testnet now - ``` - - - -
- -#### Add a message - - - - - ```bash - # Replace with your account ID - # Required a text - # Optional deposit to make the message premium - near call guestbook.near-examples.testnet add_message '{"text":"Hello Near"}' --accountId --deposit 0.1 - ``` - - - - - ```bash - # Replace with your account ID - # Required a text - # Optional deposit to make the message premium - near contract call-function as-transaction guestbook.near-examples.testnet add_message json-args '{"text":"Hello Near"}' prepaid-gas '30.0 Tgas' attached-deposit '0.1 NEAR' sign-as network-config testnet sign-with-keychain send - ``` - - - -
- -:::tip -If you're using your own account, replace `guestbook.near-examples.testnet` with your `accountId`. -::: - ---- - -## Moving Forward - -A nice way to learn is by trying to expand a contract. You can modify the guestbook example to incorporate a feature where users can give likes to messages. Additionally, implement a method to toggle the like. - - - -:::note Versioning for this article - -At the time of this writing, this example works with the following versions: - -- near-cli: `4.0.13` -- node: `18.19.1` -- rustc: `1.77.0` - -::: diff --git a/docs/tutorials/examples/xcc.md b/docs/tutorials/examples/xcc.md index 152ce26f671..a44450361cc 100644 --- a/docs/tutorials/examples/xcc.md +++ b/docs/tutorials/examples/xcc.md @@ -262,7 +262,7 @@ To interact with the contract through the console, you can use the following com ## Moving Forward -A nice way to learn is by trying to expand a contract. Modify the cross contract example to use the [guest-book](guest-book.md) +A nice way to learn is by trying to expand a contract. Modify the cross contract example to use the [guest-book](https://github.com/near-examples/guest-book-examples) contract!. In this way, you can try to make a cross-contract call that attaches money. Remember to correctly [handle the callback](/smart-contracts/anatomy/crosscontract#callback-function), and to return the money to the user in case of error. diff --git a/docs/tutorials/welcome.md b/docs/tutorials/welcome.md deleted file mode 100644 index 07c53ab2204..00000000000 --- a/docs/tutorials/welcome.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -id: welcome -hide_table_of_contents: true -title: Smart Contract Tutorials -description: "Learn about smart contracts." ---- -import Card from '@site/src/components/UI/Card'; - -Whether you're a seasoned developer looking to refine your skills or a complete beginner taking your first step into the decentralized world, you've come to the right place. Our comprehensive collection of smart contract tutorials is designed to guide you from core concepts to advanced implementations, providing the hands-on knowledge you need to build, deploy, and audit secure and efficient contracts on the NEAR blockchain. - ---- - -
- -
-
- } - title="Beginner" - description="Take your first steps and learn the basics of NEAR smart contracts." - > - - -
-
- } - title="Advanced" - description="Learn more about NEAR smart contracts with advanced tutorials." - > - - -
-
- } - title="Cross Contracts" - description="Learn how to perform cross-contract calls on NEAR." - > - - -
-
- } - title="Factories" - description="Learn how to deploy multiple contracts using a factory contract." - > - - -
-
- } - title="Zero to Hero" - description="Learn how to build a full FT or NFT contract from scratch, one step at a time." - > - - -
-
- - -
diff --git a/website/sidebars.js b/website/sidebars.js index d8803b2b1bb..d0e2a2532c7 100644 --- a/website/sidebars.js +++ b/website/sidebars.js @@ -317,23 +317,10 @@ const sidebar = { ] }, { - type: 'category', - label: 'Tutorials', - link: { - type: 'doc', - id: 'tutorials/welcome', - }, - items: [ - { - "Beginner": [ - 'tutorials/examples/count-near', - 'tutorials/examples/guest-book', - 'tutorials/examples/coin-flip', - ] - }, + "Tutorials": [ + 'smart-contracts/tutorials/basic-contracts', { "Advanced": [ - 'tutorials/examples/donation', 'tutorials/examples/near-drop', 'tutorials/examples/update-contract-migrate-state', ] diff --git a/website/src/components/UI/Card/index.jsx b/website/src/components/UI/Card/index.jsx index 7901a9c9c24..710c619ee7e 100644 --- a/website/src/components/UI/Card/index.jsx +++ b/website/src/components/UI/Card/index.jsx @@ -12,7 +12,6 @@ const Card = ({ variant = 'default', // 'default', 'icon', 'image' color = 'default', // 'default', 'mint', 'purple', 'orange' className = '', - links, ...props }) => { // Determine if card should be clickable diff --git a/website/static/assets/docs/smart-contracts/tutorials/basic-contracts.png b/website/static/assets/docs/smart-contracts/tutorials/basic-contracts.png new file mode 100644 index 00000000000..5d92f85887d Binary files /dev/null and b/website/static/assets/docs/smart-contracts/tutorials/basic-contracts.png differ diff --git a/website/static/assets/docs/tutorials/examples/coin-flip.png b/website/static/assets/docs/tutorials/examples/coin-flip.png deleted file mode 100644 index 00b997276b5..00000000000 Binary files a/website/static/assets/docs/tutorials/examples/coin-flip.png and /dev/null differ diff --git a/website/static/assets/docs/tutorials/examples/count-on-near-banner.png b/website/static/assets/docs/tutorials/examples/count-on-near-banner.png deleted file mode 100644 index b81ba064bd7..00000000000 Binary files a/website/static/assets/docs/tutorials/examples/count-on-near-banner.png and /dev/null differ diff --git a/website/static/assets/docs/tutorials/examples/count-on-near.png b/website/static/assets/docs/tutorials/examples/count-on-near.png deleted file mode 100644 index 1f7a39abb6a..00000000000 Binary files a/website/static/assets/docs/tutorials/examples/count-on-near.png and /dev/null differ diff --git a/website/static/assets/docs/tutorials/examples/donation.png b/website/static/assets/docs/tutorials/examples/donation.png deleted file mode 100644 index 03be4c4823e..00000000000 Binary files a/website/static/assets/docs/tutorials/examples/donation.png and /dev/null differ diff --git a/website/static/assets/docs/tutorials/examples/guest-book.png b/website/static/assets/docs/tutorials/examples/guest-book.png deleted file mode 100644 index 191b79b9a5e..00000000000 Binary files a/website/static/assets/docs/tutorials/examples/guest-book.png and /dev/null differ diff --git a/website/static/css/custom.scss b/website/static/css/custom.scss index a645303e01b..b006d6aa7d0 100644 --- a/website/static/css/custom.scss +++ b/website/static/css/custom.scss @@ -239,7 +239,7 @@ h2 { @media (min-width: 1201px) { flex: 1 1 calc(33.333%); - max-width: calc(33.333%); + max-width: calc(50%); } }