# Near > sidebar_label: Set up Rust and a contract skeleton --- # Source: https://docs.near.org/tutorials/crosswords/01-basics/01-set-up-skeleton.md --- 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." --- 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`: ``` use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use near_sdk::{log, near_bindgen}; // Define the default message const DEFAULT_MESSAGE: &str = "Hello"; // Define the contract structure #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize)] pub struct Contract { message: String, } // Define the default, which automatically initializes the contract impl Default for Contract{ fn default() -> Self{ Self{message: DEFAULT_MESSAGE.to_string()} } } // Implement the contract structure #[near_bindgen] impl Contract { // Public method - returns the greeting saved, defaulting to DEFAULT_MESSAGE pub fn get_greeting(&self) -> String { return self.message.clone(); } // Public method - accepts a greeting, such as "howdy", and records it pub fn set_greeting(&mut self, message: String) { // Use env::log to record logs permanently to the blockchain! log!("Saving greeting {}", message); self.message = message; } } /* ``` 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… --- # Source: https://docs.near.org/tutorials/crosswords/01-basics/02-add-functions-call.md --- 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." --- 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: ``` use near_sdk::{near, env}; const PUZZLE_NUMBER: u8 = 1; #[near(contract_state)] #[derive(Default)] pub struct Contract { crossword_solution: String } #[near] impl Contract { pub fn get_puzzle_number(&self) -> u8 { PUZZLE_NUMBER } pub fn set_solution(&mut self, solution: String) { self.crossword_solution = solution; } 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.") } } } ``` 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. --- # Source: https://docs.near.org/tutorials/crosswords/02-beginner/02-structs-enums.md --- 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." --- 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 }, } ``` --- # Source: https://docs.near.org/tutorials/crosswords/03-intermediate/02-use-seed-phrase.md --- 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 // 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 // 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. ::: --- # Source: https://docs.near.org/tutorials/crosswords/02-beginner/03-actions.md --- 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." --- 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: ``` const PRIZE_AMOUNT: NearToken = NearToken::from_yoctonear(5_000_000_000_000_000_000_000_000); #[near(serializers = [borsh, json])] ``` 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: ``` pub fn submit_solution(&mut self, solution: String, memo: String) { let hashed_input = env::sha256(solution.as_bytes()); let hashed_input_hex = hex::encode(&hashed_input); // Check to see if the hashed answer is among the puzzles let mut puzzle = self .puzzles .get(&hashed_input_hex) .expect("ERR_NOT_CORRECT_ANSWER"); // Check if the puzzle is already solved. If it's unsolved, set the status to solved, // then proceed to update the puzzle and pay the winner. puzzle.status = match puzzle.status { PuzzleStatus::Unsolved => PuzzleStatus::Solved { memo: memo.clone() }, _ => { env::panic_str("ERR_PUZZLE_SOLVED"); } }; // Reinsert the puzzle back in after we modified the status: self.puzzles.insert(&hashed_input_hex, &puzzle); // Remove from the list of unsolved ones self.unsolved_puzzles.remove(&hashed_input_hex); log!( "Puzzle with solution hash {} solved, with memo: {}", hashed_input_hex, memo ); // Transfer the prize money to the winner Promise::new(env::predecessor_account_id()).transfer(PRIZE_AMOUNT); } /// Get the hash of a crossword puzzle solution from the unsolved_puzzles ``` 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. ::: --- # Source: https://docs.near.org/tutorials/crosswords/01-basics/03-hashing-and-unit-tests.md --- 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." --- 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. ``` [dependencies] near-sdk = { version = "5.1.0", features = ["legacy"] } hex = "0.4.3" ``` 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.) ``` // use the attribute below for unit tests #[cfg(test)] mod tests { use super::*; use near_sdk::test_utils::{get_logs, VMContextBuilder}; use near_sdk::{testing_env, AccountId}; #[test] fn debug_get_hash() { // Basic set up for a unit test testing_env!(VMContextBuilder::new().build()); // Using a unit test to rapidly debug and iterate let debug_solution = "near nomicon ref finance"; let debug_hash_bytes = env::sha256(debug_solution.as_bytes()); let debug_hash_string = hex::encode(debug_hash_bytes); println!("Let's debug: {:?}", debug_hash_string); } ``` :::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: ``` // part of writing unit tests is setting up a mock context // provide a `predecessor` here, it'll modify the default context fn get_context(predecessor: AccountId) -> VMContextBuilder { let mut builder = VMContextBuilder::new(); builder.predecessor_account_id(predecessor); builder } #[test] fn check_guess_solution() { // Get Alice as an account ID let alice: AccountId = "alice.testnet".parse().unwrap(); // Set up the testing context and unit test environment let context = get_context(alice); testing_env!(context.build()); // Set up contract object and call the new method let contract = Contract::new( "69c2feb084439956193f4c21936025f14a5a5a78979d67ae34762e18a7206a0f".to_string(), ); let mut guess_result = contract.guess_solution("wrong answer here".to_string()); assert!(!guess_result, "Expected a failure from the wrong guess"); assert_eq!(get_logs(), ["Try again."], "Expected a failure log."); guess_result = contract.guess_solution("near nomicon ref finance".to_string()); assert!(guess_result, "Expected the correct answer to return true."); assert_eq!( get_logs(), ["Try again.", "You guessed right!"], "Expected a successful log after the previous failed log." ); } } ``` 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. ``` #[near] impl Contract { #[init] pub fn new(solution: String) -> Self { Self { crossword_solution: solution, } } pub fn get_solution(&self) -> String { ``` 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: ``` Promise::new(staking_pool_account_id.clone()) .create_account() .transfer(env::attached_deposit()) .deploy_contract(include_bytes!("../../staking-pool/res/staking_pool.wasm").to_vec()) .function_call( b"new".to_vec(), near_sdk::serde_json::to_vec(&StakingPoolArgs { ``` 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. ``` pub fn get_solution(&self) -> String { self.crossword_solution.clone() } pub fn guess_solution(&mut self, solution: String) -> bool { let hashed_input = env::sha256(solution.as_bytes()); let hashed_input_hex = hex::encode(&hashed_input); if hashed_input_hex == self.crossword_solution { env::log_str("You guessed right!"); true } else { env::log_str("Try again."); false } } } ``` 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. --- # Source: https://docs.near.org/tutorials/crosswords/03-intermediate/03-linkdrop.md --- 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." --- 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 Ⓝ.) ``` || options.limited_access_keys.is_some() || options.use_global_contract_hash.is_some() || options.use_global_contract_account_id.is_some(); assert!( is_some_option, "Cannot create account with no options. Please specify either contract bytes, full access keys, limited access keys, or global contract options." ); // Count contract deployment options to ensure they're mutually exclusive let contract_options_count = [ options.contract_bytes.is_some(), options.contract_bytes_base64.is_some(), options.use_global_contract_hash.is_some(), options.use_global_contract_account_id.is_some(), ] .iter() .filter(|&&x| x) .count(); assert!( contract_options_count <= 1, "Cannot specify multiple contract deployment options. Choose only one: contract_bytes, contract_bytes_base64, use_global_contract_hash, or use_global_contract_account_id." ); let amount = env::attached_deposit(); ``` 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: ``` // If there's a global contract account ID, use the existing global contract by account ID if let Some(account_id) = options.use_global_contract_account_id { promise = promise.use_global_contract_by_account_id(account_id); }; // Callback if anything went wrong, refund the predecessor for their attached deposit promise.then( Self::ext(env::current_account_id()) .with_static_gas(ON_CREATE_ACCOUNT_CALLBACK_GAS) .on_account_created(env::predecessor_account_id(), amount), ) } /// Callback after executing `create_account` or `create_account_advanced`. #[private] ``` 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: ``` accounts: near_sdk::collections::UnorderedMap::new(b"a"), } } /// Allows given public key to claim sent balance. #[payable] pub fn send(&mut self, public_key: PublicKey) -> Promise { assert!( env::attached_deposit() > NearToken::from_near(0), "Attached deposit must be at least 1 yoctoNEAR" ); let value = self ``` 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. --- # Source: https://docs.near.org/api/rpc/access-keys.md # Source: https://docs.near.org/protocol/access-keys.md --- id: access-keys title: Access Keys description: "Learn about NEAR's access key system with Full-Access Keys for complete account control and Function-Call Keys for restricted, shareable permissions to specific contracts." --- In NEAR, users control their accounts using access keys, which can be full-access keys or function-call keys. Full-access keys allow complete control over the account, while function-call keys restrict actions to specific contracts. This system enables secure sharing of permissions and simplifies user interactions with applications. :::tip Learn more about the benefits of using multiple keys in our blog post: [Benefits of Function-Call Keys](/blog/benefits-of-multiple-keys) ::: --- ## Access Keys In most blockchains, users control their accounts by holding a single [`private key`](https://en.wikipedia.org/wiki/Public-key_cryptography) (a secret only they know) and using it to sign [transactions](./transactions.md). ![img](@site/static/assets/docs/welcome-pages/access-keys.png) In NEAR we distinguish two types of Access Keys: 1. `Full-Access Keys`: Have full control over the account, and should **never be shared** 2. `Function-Call Keys`: Can only sign calls for specific contracts, and are **meant to be shared** Every account in NEAR can hold **multiple keys**, and keys can be added or removed, allowing a fine-grained control over the account's permissions. --- ## Function-Call Keys {#function-call-keys} `Function-Call` keys can only sign transactions calling a **specific contract**, and do **not allow** to **attach NEAR tokens** to the call. They are defined by three attributes: 1. `receiver_id`: The **only contract** which the key allows to call, no other contract can be called with this key 2. `method_names` (Optional): The contract's **methods** the key allows to call. If omitted, all contract's methods can be called 3. `allowance` (Optional): The **amount of NEAR** allowed to be spent on [gas](gas.md). If omitted, the key can consume **unlimited** gas `Function Call Keys` are meant to be shared with applications, so third-parties can make contract calls in your name. This is useful in [multiple scenarios as we will see below](/blog/benefits-of-multiple-keys). :::tip `Function-Call` keys are secure to share, as they only permit calls to a specific contract and prohibit NEAR token transfers ::: --- ## Full-Access Keys {#full-access-keys} As the name suggests, `Full-Access` keys have full control of an account, meaning they can be used to sign [transactions](transactions.md) doing any action in your account's behalf: 1. Transfer NEAR Ⓝ 2. Delete your account or create sub-accounts of it 3. Add or remove Access Keys 4. Deploy a smart contract in the account 5. Call methods on any contract You should never share your `Full-Access`, otherwise you are giving **total control over the account**. :::tip [Implicit accounts](./account-id.md#implicit-address) already have a `Full-Access Key` by default, while for [`named accounts`](./account-id.md#named-address) their first `Full-Access Key` is added on creation ::: --- ## Locked Accounts If you remove all keys from an account, then the account will become **locked**, meaning that no external actor can perform transactions in the account's name. In practice, this means that only the account's smart contract can transfer assets, create sub-accounts, or update its code. Locking an account is very useful when one wants to deploy a contract, and let the community be assured that only the contract is in control of the account. :::warning An account could still add keys to itself through a smart contract, effectively allowing the contract to unlock the account. Notice that this can only be done if the contract is deployed before the account is locked ::: --- # Source: https://docs.near.org/protocol/account-id.md --- id: account-id title: Address (Account ID) description: "Learn all about NEAR account addresses" --- NEAR accounts are identified by a unique address, which can take multiple forms: 1. [**Implicit address**](#implicit-address), which are 64 characters long (e.g. `fb9243ce...`) 2. [**Named address**](#named-address), which act as domains (e.g. `alice.near`, `sub.account.testnet`) 3. An ethereum-like account (e.g. `0x85f17cf997934a597031b2e18a9ab6ebd4b9f6a4`)
Valid Account IDs In NEAR, accounts can actually be any string as long as they meet the following criteria: - It must have at least 2 characters and can go up to 64 characters - It can only use lowercase letters (`a-z`), digits (`0-9`), and separators (`.`, `-`, `_`) This means that all `root`, `some-unique-string`, `something-to-remember-later`, `0x85f17....`, `fb9243ce` and `user.name` are **all valid account IDs**. However, users can only create accounts that are either a `named address`, an `implicit address`, or an `ethereum-like address`
:::tip Searching to create an account? You have multiple ways to create an account, you can create a [web wallet](https://wallet.meteorwallet.app/wallet), create a mobile wallet through [telegram](https://web.telegram.org/k/#@herewalletbot) or choose any of the available [NEAR wallets](https://wallet.near.org/). ::: --- ## Implicit Address Implicit accounts are denoted by a 64 character address, which corresponds to a unique public/private key-pair. Who controls the [private key](./access-keys.md) of the implicit account controls the account. For example: - The private key: `ed25519:4x1xiJ6u3sZF3NgrwPUCnHqup2o...` - Corresponds to the public key: `ed25519:CQLP1o1F3Jbdttek3GoRJYhzfT...` - And controls the account: `a96ad3cb539b653e4b869bd7cf26590690e8971...` Implicit accounts always *exist*, and thus do not need to be created. However, in order to use the account you will still need to fund it with NEAR tokens (or get a relayer to pay your transaction's [gas](./gas.md)). :::tip In NEAR, you can delete the private key of an implicit account, which effectively locks the account and prevents anyone to control it :::
🧑‍💻 Technical: How to obtain a key-pair The simplest way to obtain a public / private key that represents an account is using the [NEAR CLI](../tools/cli.md) ```bash near account create-account fund-later use-auto-generation save-to-folder ~/.near-credentials/implicit # The file "~/.near-credentials/implicit/8bca86065be487de45e795b2c3154fe834d53ffa07e0a44f29e76a2a5f075df8.json" was saved successfully cat ~/.near-credentials/implicit/8bca86065be487de45e795b2c3154fe834d53ffa07e0a44f29e76a2a5f075df8.json ``` or using the `near-seed-phrase` library: ```js const { seedPhrase, publicKey, secretKey } = generateSeedPhrase(); ```
🧑‍💻 Technical: How to derive an implicit account from a public key You can derive the implicit account address from a public key by removing the `ed25519:` prefix, decoding the resulting Base58 string into bytes, and then converting those bytes into a hexadecimal string. ```js // vanilla js Buffer.from(decode(publicKey.replace('ed25519:', ''))).toString('hex') // or using near-api-js utils.keyToImplicitAddress(publicKey); ```
--- ## Named Address Users can register **named accounts** (e.g. `bob.near`) which are easy to remember and share. An awesome feature of named accounts is that they can create **sub-accounts** of themselves, effectively working as domains: 1. The [`registrar`](https://nearblocks.io/address/registrar) account can create top-level accounts (e.g. `near`, `sweat`, `kaiching`). 2. The `near` account can create sub-accounts such as `bob.near` or `alice.near` 3. `bob.near` can create sub-accounts of itself, such as `app.bob.near` 4. Accounts cannot create sub-accounts of other accounts - `near` **cannot** create `app.bob.near` - `account.near` **cannot** create `sub.another-account.near` 5. Accounts have **no control** over their sub-account, they are different entities Anyone can create a `.near` or `.testnet` account, you just need to call the `create_account` method of the corresponding top-level account - `testnet` on testnet, and `near` on mainnet.
🧑‍💻 Technical: How Are Named Accounts Created? In NEAR, named accounts are created using the [`CreateAccount`](./transaction-anatomy.md#actions) action. Both `testnet` and `near` accounts expose the function `create_account`, which triggers `CreateAccount` to create sub-accounts of themselves. For example, if we want to create the account `alice.testnet` with `0.1 NEAR`, we would need to call the `create_account` method of `testnet` using an existing account (e.g. `funding-account.testnet`): ```bash near call testnet create_account '{"new_account_id": "alice.testnet", "new_public_key": "ed25519:"}' --useAccount funding-account.testnet --networkId testnet ``` If we then want to create a sub-account of `alice.testnet` - which we control - we can simply trigger the `CreateAccount` action ourselves: ```bash near create-account sub-acc.new-acc.testnet --useAccount new-acc.testnet ``` > Note: if you want the accounts to have some initial balance, you can add the `--deposit ` flag to the command above
:::info Creating Named Accounts If you are not going through an intermediary (i.e. the CLI, or a wallet), then you can create an implicit account, fund it, and call `create_account` on `near` / `testnet` ::: --- ## Ethereum-like Address NEAR also supports Ethereum-like accounts which are identified by a hexadecimal address (e.g. `0x85f17cf997934a597031b2e18a9ab6ebd4b9f6a4`). These accounts are automatically created when a user signs in on a NEAR application using an Ethereum wallet such as MetaMask. :::tip Learn More Learn about the technology behind Ethereum-like accounts in our [Blog post](/blog/hello-ethereum-wallets) ::: --- # Source: https://docs.near.org/protocol/account-model.md --- id: account-model title: NEAR Accounts sidebar_label: Overview description: "Learn about NEAR Protocol's account model, including named and implicit accounts, access keys, permissions, and how NEAR accounts differ from other blockchain platforms." --- Users participate in the NEAR ecosystem through their NEAR accounts. These accounts are identified by a [unique address](./account-id.md), can optionally hold a [smart contract](../smart-contracts/what-is.md), and are controlled through [Access Keys](./access-keys.md). By signing [transactions](./transactions.md) with their account, users can: 1. Send and receive **digital assets** (such as tokens or collectibles) 2. Create and interact with on-chain applications known as **smart contracts** 3. Control accounts in **other chains** (such as Ethereum or Bitcoin) ✨ 4. Help onboard new users by **covering the costs** of their transactions (gas fees) :::tip Want to create an account? Check out our tutorial on [Creating a NEAR Account](../tutorials/protocol/create-account.md) to get started! ::: --- ## Account Model Overview Let's take a closer look at the different elements that compose the NEAR account model. ![img](@site/static/assets/docs/welcome-pages/accounts.png) ### [Account ID](account-id.md) NEAR **natively** implements multiple types of accounts, including: 1. **Named accounts** such as `alice.near`, which are simple to remember and share 2. **Implicit accounts** such as `fb9243ce...`, which are derived from a private key 2. **Ethereum-like accounts** which are compatible with Ethereum wallets
### [Multiple Keys](access-keys.md) NEAR accounts can have multiple [keys](access-keys.md), each with their own set of permissions: - You can easily swap keys if one gets compromised - You can use keys as authorization tokens for third-party applications
### [Smart Contracts](../smart-contracts/what-is.md) NEAR accounts can optionally hold an application - known as a [smart contract](../smart-contracts/what-is.md) - which can be written in Javascript or Rust. --- ## Comparison With Ethereum {#compared-to-ethereum} If you're familiar with development on Ethereum, it's worth making a quick note about how accounts are different. The table below summarizes some key differences: | | Ethereum Account | NEAR Account | |-----------------|--------------------------|----------------------------------------------------------------------------------------| | Account ID | Public Key (`0x123...`) | - Native named accounts (`alice.near`)
- Implicit accounts (`0x123...`) | | Secret Key | Private Key (`0x456...`) | Multiple key-pairs with permissions:
- `FullAccess` key
- `FunctionCall` key | | Smart Contracts | Synchronous execution | Asynchronous execution | | Gas Fees | In the order of dollars | In the order of tenths of a cent | | Block Time | ~12 seconds | ~600 milliseconds | --- # Source: https://docs.near.org/integrations/accounts.md --- id: accounts title: Accounts sidebar_label: Accounts description: "Learn about NEAR account management for exchanges and integrations, including account creation, key management, and balance tracking." --- This document provides relevant information about NEAR accounts for exchanges looking forward to integrate the NEAR token Please see the [documentation for accounts](/protocol/account-model) for basic information. - For exchanges, NEAR supports [implicit account](https://nomicon.io/DataStructures/Account.html#implicit-account-ids) creation which allows the creation of accounts without paying for transactions. - You can create an implicit account by following the steps in [this guide](/integrations/implicit-accounts). - Accounts must have enough tokens to cover its storage which currently costs `0.0001 NEAR` per byte. This equates to a minimum balance of `0.00182 NEAR` for an account with one access key. You can query the live storage price using the [`protocol-config`](/api/rpc/protocol#protocol-config) RPC endpoint. For more details on storage fees see [this section of the economics paper](https://pages.near.org/papers/economics-in-sharded-blockchain/#transaction-and-storage-fees). ## Transfer from Function Call {#transfer-from-function-call} NEAR allows transfers to happen within a function call. More importantly, when an account is deployed with some contract, it is possible that the only way to transfer tokens from that account is through a function call. Therefore, exchanges need to support transfers through function calls as well. We recommend the following approach: Exchange can [query block by height](/api/rpc/block-chunk#block-details) to get blocks on each height, and for every block, [query its chunk](/api/rpc/block-chunk#chunk-details) to obtain the transactions included in the block. For each transaction, [query its status](/api/rpc/transactions#transaction-status-with-receipts) to see the receipts generated from transactions. Since exchanges are only interested in transfers to their addresses, they only need to filter receipts that only contain `Transfer` action and whose `predecessor_id` is not `system` (receipts with `predecessor_id` equal to `system` are [refunds](https://nomicon.io/RuntimeSpec/Refunds.html)). Then, to check whether the receipt succeeds, it is sufficient to look for the `receipt_id` in `receipts_outcome` and see if its status is `SuccessValue`. Alternatively, exchange can use [the indexer framework](https://github.com/near/nearcore/tree/master/chain/indexer) to help index on-chain data which include receipts. An example usage of the indexer can be found [here](https://github.com/near/nearcore/tree/master/tools/indexer/example). Below we include examples from the contracts that are likely to be used to perform transfers through function calls. **Example of transfer from a lockup contract** A contract `evgeny.lockup.near` is deployed and we can check its owner by ```bash near contract call-function as-read-only evgeny.lockup.near get_owner_account_id json-args '{}' network-config testnet now # View call: evgeny.lockup.near.get_owner_account_id() # 'evgeny.near' ``` Now we want to transfer some unlocked tokens (1 NEAR) with the following call ```bash near contract call-function as-transaction evgeny.lockup.near transfer json-args '{"amount":"1000000000000000000000000", "receiver_id": "evgeny.near"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as evgeny.near network-config testnet sign-with-keychain send ``` **Note**: the response below can be obtained by hitting the RPC with the transaction hash and NEAR account like this: ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=txstatus method=EXPERIMENTAL_tx_status \ params:='[ "GXP8YaSonoN2eBY6dB3FbMN2NyYD2JeJJvKdvbL4Jmb2", "evgeny.near"]' ```
**Example Response:** ```json { "id": "123", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "evgeny.near", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwicmVjZWl2ZXJfaWQiOiJldmdlbnkubmVhciJ9", "deposit": "0", "gas": 100000000000000, "method_name": "transfer" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "evgeny.near", "signer_public_key": "ed25519:BVRTxEEMx3gFceSgJtnvPFbSnPDwwUzHe6KGduRh5Byq" } }, "receipt_id": "CyJL22SYqt26qgh2XVnk9MGfvzgyiiq5Lny7DdbTdTWU", "receiver_id": "evgeny.lockup.near" }, { "predecessor_id": "evgeny.lockup.near", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "1000000000000000000000000" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "evgeny.near", "signer_public_key": "ed25519:BVRTxEEMx3gFceSgJtnvPFbSnPDwwUzHe6KGduRh5Byq" } }, "receipt_id": "EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC", "receiver_id": "evgeny.near" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "19200274886926125000" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "evgeny.near", "signer_public_key": "ed25519:BVRTxEEMx3gFceSgJtnvPFbSnPDwwUzHe6KGduRh5Byq" } }, "receipt_id": "J1bBKH43nXHYg4NuS97R1PFzdZchrJboVAdRsK5NRrAv", "receiver_id": "evgeny.near" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "18655658845681462514128" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "evgeny.near", "signer_public_key": "ed25519:BVRTxEEMx3gFceSgJtnvPFbSnPDwwUzHe6KGduRh5Byq" } }, "receipt_id": "6PFaxnNvK5r6qxBq5WfV9uGjoNM6qjhHwLehLP1qak9d", "receiver_id": "evgeny.near" } ], "receipts_outcome": [ { "block_hash": "9boEKq9G1UFsEuzmuQrxh5dkRc8xsv8PSPGEkYiTyRLj", "id": "CyJL22SYqt26qgh2XVnk9MGfvzgyiiq5Lny7DdbTdTWU", "outcome": { "executor_id": "evgeny.lockup.near", "gas_burnt": 3574640311481, "logs": [ "Transferring 1000000000000000000000000 to account @evgeny.near" ], "receipt_ids": [ "EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC", "6PFaxnNvK5r6qxBq5WfV9uGjoNM6qjhHwLehLP1qak9d" ], "status": { "SuccessReceiptId": "EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC" }, "tokens_burnt": "357464031148100000000" }, "proof": [] }, { "block_hash": "7qn4BjmMD4QbyVvMa8QEzm7h5YuhoGTFTgLeNMUp85UQ", "id": "EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC", "outcome": { "executor_id": "evgeny.near", "gas_burnt": 223182562500, "logs": [], "receipt_ids": ["J1bBKH43nXHYg4NuS97R1PFzdZchrJboVAdRsK5NRrAv"], "status": { "SuccessValue": "" }, "tokens_burnt": "22318256250000000000" }, "proof": [ { "direction": "Right", "hash": "AwHdk5dushTSXHFBt3R5MiexjiXybwdnEaB7L9iJ5F6t" } ] }, { "block_hash": "46788Ay85YGnQaH5tfbboQNWJs3gyXsPbcWzRyxqw56K", "id": "J1bBKH43nXHYg4NuS97R1PFzdZchrJboVAdRsK5NRrAv", "outcome": { "executor_id": "evgeny.near", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [] }, { "block_hash": "7qn4BjmMD4QbyVvMa8QEzm7h5YuhoGTFTgLeNMUp85UQ", "id": "6PFaxnNvK5r6qxBq5WfV9uGjoNM6qjhHwLehLP1qak9d", "outcome": { "executor_id": "evgeny.near", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Left", "hash": "9RRJpH5VdDxsHpp323EshcAauV5wUNDyW9FpEJBRXXq8" } ] } ], "status": { "SuccessValue": "" }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIiwicmVjZWl2ZXJfaWQiOiJldmdlbnkubmVhciJ9", "deposit": "0", "gas": 100000000000000, "method_name": "transfer" } } ], "hash": "GXP8YaSonoN2eBY6dB3FbMN2NyYD2JeJJvKdvbL4Jmb2", "nonce": 6, "public_key": "ed25519:BVRTxEEMx3gFceSgJtnvPFbSnPDwwUzHe6KGduRh5Byq", "receiver_id": "evgeny.lockup.near", "signature": "ed25519:4nfzTMpQJKCY3KaqUTFig4Xy9uxwbMeQpMJjtNKsXmwiVqgcVSWRguZEgZM8L2x1jvdpZHsYjLCxc9cSBamXuXPH", "signer_id": "evgeny.near" }, "transaction_outcome": { "block_hash": "4u7maz2U43W4DPxqQE8KoRNi5dTRHrAsKsFk2qDQsQEw", "id": "GXP8YaSonoN2eBY6dB3FbMN2NyYD2JeJJvKdvbL4Jmb2", "outcome": { "executor_id": "evgeny.near", "gas_burnt": 2428086459116, "logs": [], "receipt_ids": ["CyJL22SYqt26qgh2XVnk9MGfvzgyiiq5Lny7DdbTdTWU"], "status": { "SuccessReceiptId": "CyJL22SYqt26qgh2XVnk9MGfvzgyiiq5Lny7DdbTdTWU" }, "tokens_burnt": "242808645911600000000" }, "proof": [] } } } ```
As we can see, there are four receipts generated in this function call. If we apply the criteria mentioned above, we can find in `receipts` field this object ```json { "predecessor_id": "evgeny.lockup.near", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "1000000000000000000000000" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "evgeny.near", "signer_public_key": "ed25519:BVRTxEEMx3gFceSgJtnvPFbSnPDwwUzHe6KGduRh5Byq" } }, "receipt_id": "EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC", "receiver_id": "evgeny.near" } ``` which contains only `Transfer` action and whose `predecessor_id` is not `system`. Now we can check the status of the execution by looking for the same receipt id `EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC` in `receipts_outcome` field of the rpc return result, this leads us to this execution outcome ```json { "block_hash": "7qn4BjmMD4QbyVvMa8QEzm7h5YuhoGTFTgLeNMUp85UQ", "id": "EvHfj4fUyVuLBRKNdCZmFGr4WfqwYf7YCbzFsRGFTFJC", "outcome": { "executor_id": "evgeny.near", "gas_burnt": 223182562500, "logs": [], "receipt_ids": ["J1bBKH43nXHYg4NuS97R1PFzdZchrJboVAdRsK5NRrAv"], "status": { "SuccessValue": "" }, "tokens_burnt": "22318256250000000000" }, "proof": [ { "direction": "Right", "hash": "AwHdk5dushTSXHFBt3R5MiexjiXybwdnEaB7L9iJ5F6t" } ] } ``` and its status contains `SuccessValue`, which indicates that the receipt has succeeded. Therefore we know that `1000000000000000000000000` yoctoNEAR, or 1 NEAR has been successfully transferred. **Example of transfer from a multisig contract** Multisig contract, as the name suggests, uses multiple signatures to confirm a transaction and therefore, actions performed by the multisig contract involve multiple transactions. In the following example, we will show how a transfer is done from a multisig contract that requires two confirmations. - First step: `add_request_and_confirm`. This initiates the action that the multisig contract wants to perform with one confirmation. The multisig contract `multisigtest.testnet` wants to transfer 1 NEAR to `bowen` and it first sends a transaction that calls `add_request_and_confirm` with a request ```json { "request": { "receiver_id": "bowen", "actions": [ { "type": "Transfer", "amount": "1000000000000000000000000" } ] } } ``` that indicates it wants to transfer 1 NEAR to `bowen`. Notice that this transaction only records the action but does not perform the actual transfer. The transaction result is as follows:
**Example Response:** ```json { "id": "123", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "3069687780141648922140" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "multisigtest.testnet", "signer_public_key": "ed25519:JDewsbE7nz6orFD4zJ3mVzqhfcaoSD6Hmi5as3AHHiTt" } }, "receipt_id": "4qgDptd7Wm6vswAhWMCsVpTjBEkmLJEUxSNVQS1wu3rD", "receiver_id": "multisigtest.testnet" } ], "receipts_outcome": [ { "block_hash": "6uJWHTvUrtFQAurUyfuAfy9EdoR9FhLodGh44aHJta6m", "id": "94LiYwKJEDherHMNg9fqLy9ShFTDiQiUN3nDaGmLZwth", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 8024094920263, "logs": [], "receipt_ids": ["4qgDptd7Wm6vswAhWMCsVpTjBEkmLJEUxSNVQS1wu3rD"], "status": { "SuccessValue": "OA==" }, "tokens_burnt": "802409492026300000000" }, "proof": [ { "direction": "Left", "hash": "GedzmwRkxA5VkT8GLBCnrPUmnEhWPXadPmiq4Ho1s9pH" }, { "direction": "Right", "hash": "GirkzdS9YpsAz5fXuL5T3rXd93aRcnXNAdXYi241qpWK" } ] }, { "block_hash": "4JyQ6guJKeWZxxXrKndLDuSa5URuirmBi6RzsbKYFsBE", "id": "4qgDptd7Wm6vswAhWMCsVpTjBEkmLJEUxSNVQS1wu3rD", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [] } ], "status": { "SuccessValue": "OA==" }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJyZXF1ZXN0Ijp7InJlY2VpdmVyX2lkIjoiYm93ZW4iLCJhY3Rpb25zIjpbeyJ0eXBlIjoiVHJhbnNmZXIiLCJhbW91bnQiOiIxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwIn1dfX0=", "deposit": "0", "gas": 30000000000000, "method_name": "add_request_and_confirm" } } ], "hash": "FGREJkC1e8y95Rc35iD1LVRiDy1WcAZhAxxkSinfb2mL", "nonce": 10, "public_key": "ed25519:JDewsbE7nz6orFD4zJ3mVzqhfcaoSD6Hmi5as3AHHiTt", "receiver_id": "multisigtest.testnet", "signature": "ed25519:3NUKXd4uj2eEBqGQtRAxkTFW7UfG44tjvQNNHBDvN9ZswTTMRsDrMJSd1U3GqWF7QToqWQR9J8atNEVTemSWYw41", "signer_id": "multisigtest.testnet" }, "transaction_outcome": { "block_hash": "6uJWHTvUrtFQAurUyfuAfy9EdoR9FhLodGh44aHJta6m", "id": "FGREJkC1e8y95Rc35iD1LVRiDy1WcAZhAxxkSinfb2mL", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 2428204963618, "logs": [], "receipt_ids": ["94LiYwKJEDherHMNg9fqLy9ShFTDiQiUN3nDaGmLZwth"], "status": { "SuccessReceiptId": "94LiYwKJEDherHMNg9fqLy9ShFTDiQiUN3nDaGmLZwth" }, "tokens_burnt": "242820496361800000000" }, "proof": [ { "direction": "Right", "hash": "AsNAQabPFkmaugRGhCbzcEcR8Gnd22WXxPM2fb2cwHiv" }, { "direction": "Right", "hash": "GirkzdS9YpsAz5fXuL5T3rXd93aRcnXNAdXYi241qpWK" } ] } } } ```
- Second step: `confirm`. A second transaction is sent to confirm the transfer. This transaction takes the request id returned by the first transaction and does the actual transfer. The transaction result is as follows
**Example Response:** ```json { "id": "123", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "multisigtest.testnet", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "1000000000000000000000000" } } ], "gas_price": "451542320", "input_data_ids": [], "output_data_receivers": [], "signer_id": "multisigtest.testnet", "signer_public_key": "ed25519:BmVX32jhvEd8d8outiQdjf66GGYV3pb7kaxrKTdNisCz" } }, "receipt_id": "DZbHTEf3i3XznK4oJHQfcrteoiCL6WykRiA8vsn4LmAy", "receiver_id": "bowen" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "78458115804795000000" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "multisigtest.testnet", "signer_public_key": "ed25519:BmVX32jhvEd8d8outiQdjf66GGYV3pb7kaxrKTdNisCz" } }, "receipt_id": "6SxC9GfYdjqm7Ao5EAw51XUAjgoN8Lj2X9xJfxjDQYXd", "receiver_id": "multisigtest.testnet" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "112870156274913516718240" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "multisigtest.testnet", "signer_public_key": "ed25519:BmVX32jhvEd8d8outiQdjf66GGYV3pb7kaxrKTdNisCz" } }, "receipt_id": "CHfzz6NLcQMyiLHBQoczhgm5BFjLVfv9B7eCyXKLhhcT", "receiver_id": "multisigtest.testnet" } ], "receipts_outcome": [ { "block_hash": "9JEiMrZ1SpAUEbQswde3Diptzwy35Vrvd41VZWG9hYVE", "id": "FfuhYhsgFL7sLC8pk1tuRnMHJdqycE6gEcfgZLW9fmFB", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 10109796553814, "logs": [], "receipt_ids": [ "DZbHTEf3i3XznK4oJHQfcrteoiCL6WykRiA8vsn4LmAy", "CHfzz6NLcQMyiLHBQoczhgm5BFjLVfv9B7eCyXKLhhcT" ], "status": { "SuccessReceiptId": "DZbHTEf3i3XznK4oJHQfcrteoiCL6WykRiA8vsn4LmAy" }, "tokens_burnt": "1010979655381400000000" }, "proof": [ { "direction": "Left", "hash": "9e2UcG6qBRahBh3V2Z8bGJLh5c4jXfZdP3WBJkCpJCfj" } ] }, { "block_hash": "4LkVfqyhhrxDdVFmow6NxLf1jTaj6XVr7CVcUxxySd1R", "id": "DZbHTEf3i3XznK4oJHQfcrteoiCL6WykRiA8vsn4LmAy", "outcome": { "executor_id": "bowen", "gas_burnt": 223182562500, "logs": [], "receipt_ids": ["6SxC9GfYdjqm7Ao5EAw51XUAjgoN8Lj2X9xJfxjDQYXd"], "status": { "SuccessValue": "" }, "tokens_burnt": "22318256250000000000" }, "proof": [ { "direction": "Right", "hash": "FFWaWUFt6sNx5XNHzGYsYBSYFWtGPoww5XQz1QmLVc8i" } ] }, { "block_hash": "G6LDdnAa2b38TB4KZ89HAyVgfgyiRPDDgSxoZypbUYpx", "id": "6SxC9GfYdjqm7Ao5EAw51XUAjgoN8Lj2X9xJfxjDQYXd", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [] }, { "block_hash": "4LkVfqyhhrxDdVFmow6NxLf1jTaj6XVr7CVcUxxySd1R", "id": "CHfzz6NLcQMyiLHBQoczhgm5BFjLVfv9B7eCyXKLhhcT", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Left", "hash": "DpDYAEKZTtSomgyeNcJ2i4qjvfqnCtf1CXa83Cz5wvEy" } ] } ], "status": { "SuccessValue": "" }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJyZXF1ZXN0X2lkIjo4fQ==", "deposit": "0", "gas": 250000000000000, "method_name": "confirm" } } ], "hash": "Fu39vwxC4mu9ks1DZA5Cib63RnBMHpFonk2DcbpioEYc", "nonce": 9, "public_key": "ed25519:BmVX32jhvEd8d8outiQdjf66GGYV3pb7kaxrKTdNisCz", "receiver_id": "multisigtest.testnet", "signature": "ed25519:2raQq7t3cmzSL2krE2xaNqXhAw7cKMoXrBjT2ZhAGfCVtGwzbbQ8zkB17vrCSFZDbFmPWSJpoqsw8qPZZiorwSzS", "signer_id": "multisigtest.testnet" }, "transaction_outcome": { "block_hash": "9JEiMrZ1SpAUEbQswde3Diptzwy35Vrvd41VZWG9hYVE", "id": "Fu39vwxC4mu9ks1DZA5Cib63RnBMHpFonk2DcbpioEYc", "outcome": { "executor_id": "multisigtest.testnet", "gas_burnt": 2427972426482, "logs": [], "receipt_ids": ["FfuhYhsgFL7sLC8pk1tuRnMHJdqycE6gEcfgZLW9fmFB"], "status": { "SuccessReceiptId": "FfuhYhsgFL7sLC8pk1tuRnMHJdqycE6gEcfgZLW9fmFB" }, "tokens_burnt": "242797242648200000000" }, "proof": [ { "direction": "Right", "hash": "B6hN48qeVP8J3hP8XGcANShM264QkNjgJAfMtsuknqex" } ] } } } ```
Notice that similar to the transfer from lockup contract, there is also one receipt in the `receipts` field that meet our requirements: ```json { "predecessor_id": "multisigtest.testnet", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "1000000000000000000000000" } } ], "gas_price": "451542320", "input_data_ids": [], "output_data_receivers": [], "signer_id": "multisigtest.testnet", "signer_public_key": "ed25519:BmVX32jhvEd8d8outiQdjf66GGYV3pb7kaxrKTdNisCz" } }, "receipt_id": "DZbHTEf3i3XznK4oJHQfcrteoiCL6WykRiA8vsn4LmAy", "receiver_id": "bowen" } ``` and we can find its outcome in `receipts_outcome`: ```json { "block_hash": "4LkVfqyhhrxDdVFmow6NxLf1jTaj6XVr7CVcUxxySd1R", "id": "DZbHTEf3i3XznK4oJHQfcrteoiCL6WykRiA8vsn4LmAy", "outcome": { "executor_id": "bowen", "gas_burnt": 223182562500, "logs": [], "receipt_ids": ["6SxC9GfYdjqm7Ao5EAw51XUAjgoN8Lj2X9xJfxjDQYXd"], "status": { "SuccessValue": "" }, "tokens_burnt": "22318256250000000000" }, "proof": [ { "direction": "Right", "hash": "FFWaWUFt6sNx5XNHzGYsYBSYFWtGPoww5XQz1QmLVc8i" } ] } ``` which indicates that the transaction is successful. :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/smart-contracts/anatomy/actions.md --- id: actions title: Transfers & Actions description: "Learn how contracts can make transfers, call other contracts, and more" --- This page describes the different types of actions that a smart contract can perform on NEAR like transferring NEAR, calling other contracts, creating sub-accounts, and deploying contracts. It also explains how to add access keys to accounts. Smart contracts can perform specific `Actions` such as transferring NEAR, or calling other contracts. An important property of `Actions` is that they can be batched together when acting on the same contract. **Batched actions** act as a unit: they execute in the same [receipt](/protocol/transaction-execution#receipts--finality), and if **any fails**, then they **all get reverted**. :::info `Actions` can be batched only when they act on the **same contract**. You can batch calling two methods on a contract, but **cannot** call two methods on different contracts. ::: --- ## Transfer NEAR Ⓝ You can send $NEAR from your contract to any other account on the network. The Gas cost for transferring $NEAR is fixed and is based on the protocol's genesis config. Currently, it costs `~0.45 TGas`. ```rust use near_sdk::{near, AccountId, Promise, NearToken}; #[near(contract_state)] #[derive(Default)] pub struct Contract { } #[near] impl Contract { pub fn transfer(&self, to: AccountId, amount: NearToken){ Promise::new(to).transfer(amount); } } ``` ```js import { NearBindgen, NearPromise, call } from 'near-sdk-js' import { AccountId } from 'near-sdk-js/lib/types' @NearBindgen({}) class Contract{ @call({}) transfer({ to, amount }: { to: AccountId, amount: bigint }) { return NearPromise.new(to).transfer(amount); } } ``` ```python from near_sdk_py import call, Contract from near_sdk_py.promises import Promise class ExampleContract(Contract): @call def transfer(self, to, amount): """Transfers NEAR to another account""" # Create a promise to transfer NEAR return Promise.create_batch(to).transfer(amount) ``` ```go package main "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type TransferTokenInput struct { To string `json:"to"` Amount string `json:"amount"` } // @contract:payable min_deposit=1NEAR func (c *Contract) ExampleTransferToken(input TransferTokenInput) error { amount, err := types.U128FromString(input.Amount) if err != nil { return err } promise.CreateBatch(input.To). Transfer(amount) return nil } ``` :::tip Why is there no callback? The only case where a transfer will fail is if the receiver account does **not** exist. ::: :::caution Remember that your balance is used to cover for the contract's storage. When sending money, make sure you always leave enough to cover for future storage needs. ::: --- ## Function Call Your smart contract can call methods in another contract. In the snippet below we call a method in a deployed [Hello NEAR](../quickstart.md) contract, and check if everything went right in the callback. ```rust use near_sdk::{near, env, log, Promise, Gas, PromiseError}; use serde_json::json; #[near(contract_state)] #[derive(Default)] pub struct Contract { } const HELLO_NEAR: &str = "hello-nearverse.testnet"; const NO_DEPOSIT: u128 = 0; const CALL_GAS: Gas = Gas(5_000_000_000_000); #[near] impl Contract { pub fn call_method(&self){ let args = json!({ "message": "howdy".to_string() }) .to_string().into_bytes().to_vec(); Promise::new(HELLO_NEAR.parse().unwrap()) .function_call("set_greeting".to_string(), args, NO_DEPOSIT, CALL_GAS) .then( Promise::new(env::current_account_id()) .function_call("callback".to_string(), Vec::new(), NO_DEPOSIT, CALL_GAS) ); } pub fn callback(&self, #[callback_result] result: Result<(), PromiseError>){ if result.is_err(){ log!("Something went wrong") }else{ log!("Message changed") } } } ``` ```js import { NearBindgen, near, call, bytes, NearPromise } from 'near-sdk-js' import { AccountId } from 'near-sdk-js/lib/types' const HELLO_NEAR: AccountId = "hello-nearverse.testnet"; const NO_DEPOSIT: bigint = BigInt(0); const CALL_GAS: bigint = BigInt("10000000000000"); @NearBindgen({}) class Contract { @call({}) call_method({}): NearPromise { const args = bytes(JSON.stringify({ message: "howdy" })) return NearPromise.new(HELLO_NEAR) .functionCall("set_greeting", args, NO_DEPOSIT, CALL_GAS) .then( NearPromise.new(near.currentAccountId()) .functionCall("callback", bytes(JSON.stringify({})), NO_DEPOSIT, CALL_GAS) ) .asReturn() } @call({privateFunction: true}) callback({}): boolean { let result, success; try{ result = near.promiseResult(0); success = true } catch{ result = undefined; success = false } if (success) { near.log(`Success!`) return true } else { near.log("Promise failed...") return false } } } ``` ```python from near_sdk_py import call, callback, Contract from near_sdk_py.promises import CrossContract, Promise, PromiseResult from near_sdk_py.constants import ONE_TGAS # Constants HELLO_NEAR = "hello-nearverse.testnet" NO_DEPOSIT = 0 CALL_GAS = 10 * ONE_TGAS class ExampleContract(Contract): @call def call_method(self): """Call a method on an external contract with a callback""" # Create a contract reference hello = CrossContract(HELLO_NEAR) # Call the external contract and use our callback to process the result return hello.call("set_greeting", {"message": "howdy"}).then("callback") @callback def callback(self, result: PromiseResult): """Handle the result of the external contract call""" # The @callback decorator automatically handles success/failure checking if not result.success: # The remote call failed self.log_error("Promise failed...") return False # The remote call succeeded self.log_info("Success!") return True ``` ```go package main "strconv" "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type MessageInput struct { Message string `json:"message"` } // @contract:payable min_deposit=0.00001NEAR func (c *Contract) ExampleFunctionCall() { gas := uint64(types.ONE_TERA_GAS * 10) accountId := "hello-nearverse.testnet" args := map[string]string{ "message": "howdy", } promise.NewCrossContract(accountId). Gas(gas). Call("set_greeting", args). Then("example_function_call_callback", args) } // @contract:view // @contract:promise_callback func (c *Contract) ExampleFunctionCallCallback(input MessageInput, result promise.PromiseResult) MessageInput { env.LogString("Executing callback") env.LogString("Input Message : " + input.Message) if result.Success { env.LogString("Cross-contract call executed successfully") env.LogString("Promise Result Status --> " + strconv.FormatInt(int64(result.StatusCode), 10)) if len(result.Data) > 0 { env.LogString("Batch call data: " + string(result.Data)) } } else { env.LogString("Cross-contract call failed") } return input } ``` :::warning The snippet showed above is a low level way of calling other methods. We recommend make calls to other contracts as explained in the [Cross-contract Calls section](crosscontract.md). ::: --- ## Create a Sub Account Your contract can create direct sub accounts of itself, for example, `user.near` can create `sub.user.near`. Accounts do **NOT** have control over their sub-accounts, since they have their own keys. Sub-accounts are simply useful for organizing your accounts (e.g. `dao.project.near`, `token.project.near`). ```rust use near_sdk::{near, env, Promise, NearToken}; #[near(contract_state)] #[derive(Default)] pub struct Contract { } const MIN_STORAGE: NearToken = NearToken::from_millinear(1); //0.001Ⓝ #[near] impl Contract { pub fn create(&mut self, prefix: String) { let account_id = prefix + "." + &env::current_account_id().to_string(); Promise::new(account_id.parse().unwrap()) .create_account() .transfer(MIN_STORAGE); } } ``` ```js import { NearBindgen, near, call, NearPromise } from 'near-sdk-js' const MIN_STORAGE: bigint = BigInt("1000000000000000000000") // 0.001Ⓝ @NearBindgen({}) class Contract { @call({payableFunction:true}) create({prefix}:{prefix: String}) { const account_id = `${prefix}.${near.currentAccountId()}` NearPromise.new(account_id) .createAccount() .transfer(MIN_STORAGE) } } ``` ```python from near_sdk_py import call, Contract from near_sdk_py.promises import Promise from near_sdk_py.constants import ONE_NEAR # Minimum amount needed for storage MIN_STORAGE = ONE_NEAR // 1000 # 0.001Ⓝ class ExampleContract(Contract): @call def create(self, prefix): """Create a subaccount""" # Generate the new account ID account_id = f"{prefix}.{self.current_account_id}" # Create the new account and transfer some NEAR for storage return Promise.create_batch(account_id)\ .create_account()\ .transfer(MIN_STORAGE) ``` ```go package main "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} // @contract:payable min_deposit=0.001NEAR func (c *Contract) ExampleCreateSubaccount(prefix string) { currentAccountId, err := env.GetCurrentAccountId() if err != nil { env.PanicStr("Failed to get current account") } subaccountId := prefix + "." + currentAccountId amount, err := types.U128FromString("1000000000000000000000") //0.001Ⓝ if err != nil { env.PanicStr("Bad amount format") } promise.CreateBatch(subaccountId). CreateAccount(). Transfer(amount) } ``` :::tip Notice that in the snippet we are transferring some money to the new account for storage ::: :::caution When you create an account from within a contract, it has no keys by default. If you don't explicitly [add keys](#add-keys) to it or [deploy a contract](#deploy-a-contract) on creation then it will be [locked](../../protocol/access-keys.md#locked-accounts). :::
#### Creating `.testnet` / `.near` Accounts Accounts can only create immediate sub-accounts of themselves. If your contract wants to create a `.mainnet` or `.testnet` account, then it needs to [call](#function-call) the `create_account` method of `near` or `testnet` root contracts. ```rust use near_sdk::{near, Promise, Gas, NearToken }; use serde_json::json; #[near(contract_state)] #[derive(Default)] pub struct Contract { } const CALL_GAS: Gas = Gas::from_gas(28_000_000_000_000); const MIN_STORAGE: NearToken = NearToken::from_yoctonear(1_820_000_000_000_000_000_000); //0.00182Ⓝ #[near] impl Contract { pub fn create_account(&mut self, account_id: String, public_key: String){ let args = json!({ "new_account_id": account_id, "new_public_key": public_key, }).to_string().into_bytes().to_vec(); // Use "near" to create mainnet accounts Promise::new("testnet".parse().unwrap()) .function_call("create_account".to_string(), args, MIN_STORAGE, CALL_GAS); } } ``` ```js import { NearBindgen, near, call, bytes, NearPromise } from 'near-sdk-js' const MIN_STORAGE: bigint = BigInt("1820000000000000000000"); //0.00182Ⓝ const CALL_GAS: bigint = BigInt("28000000000000"); @NearBindgen({}) class Contract { @call({}) create_account({account_id, public_key}:{account_id: String, public_key: String}) { const args = bytes(JSON.stringify({ "new_account_id": account_id, "new_public_key": public_key })) NearPromise.new("testnet") .functionCall("create_account", args, MIN_STORAGE, CALL_GAS); } } ``` ```python from near_sdk_py import call, Contract from near_sdk_py.promises import Promise from near_sdk_py.constants import ONE_NEAR, ONE_TGAS # Constants MIN_STORAGE = int(1.82 * ONE_NEAR / 1000) # 0.00182Ⓝ CALL_GAS = 28 * ONE_TGAS class ExampleContract(Contract): @call def create_account(self, account_id, public_key): """Create a testnet account by calling the testnet contract""" # Create the arguments for the create_account method args = { "new_account_id": account_id, "new_public_key": public_key } # Call the testnet contract to create the account return Promise.create_batch("testnet")\ .function_call( "create_account", args, MIN_STORAGE, CALL_GAS ) ``` ```go package main "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type CreateAccountInput struct { AccountId string `json:"account_id"` PublicKey string `json:"public_key"` } // @contract:payable min_deposit=0.002NEAR func (c *Contract) ExampleCreateAccount(args CreateAccountInput) { amount, _ := types.U128FromString("2000000000000000000000") // 0.002 NEAR gas := uint64(200 * types.ONE_TERA_GAS) //publicKey (base58) - 4omJwNS1WbniWtbPkLYBrFwN3YLeffXCkpvriYgeLhst (generate your own for testing) //accountId - nearsdkdocs1.testnet (write your own for testing) createArgs := map[string]string{ "new_account_id": args.AccountId, "new_public_key": args.PublicKey, } promise.CreateBatch("testnet"). FunctionCall("create_account", createArgs, amount, gas) } ``` --- ## Deploy a Contract When creating an account you can also batch the action of deploying a contract to it. Note that for this, you will need to pre-load the byte-code you want to deploy in your contract. ```rust use near_sdk::{near, env, Promise, NearToken}; #[near(contract_state)] #[derive(Default)] pub struct Contract { } const MIN_STORAGE: NearToken = NearToken::from_millinear(1100); //1.1Ⓝ const HELLO_CODE: &[u8] = include_bytes!("./hello.wasm"); #[near] impl Contract { pub fn create_hello(&self, prefix: String){ let account_id = prefix + "." + &env::current_account_id().to_string(); Promise::new(account_id.parse().unwrap()) .create_account() .transfer(MIN_STORAGE) .deploy_contract(HELLO_CODE.to_vec()); } } ``` ```python from near_sdk_py import call, Context, Contract from near_sdk_py.promises import Promise from near_sdk_py.constants import ONE_NEAR class ExampleContract(Contract): @call def deploy_contract(self, prefix): """Create an account and deploy a contract to it""" # This would require loading the contract bytes in Python # Load contract bytes - for example purposes only # In a real implementation, you'd need to read this from storage or include it contract_bytes = b'...' # This should be actual WASM bytes MIN_STORAGE = 1.1 * ONE_NEAR # 1.1Ⓝ # Generate the new account ID account_id = f"{prefix}.{self.current_account_id}" # Create batch of actions return Promise.create_batch(account_id)\ .create_account()\ .transfer(MIN_STORAGE)\ .deploy_contract(contract_bytes) ``` ```go package main _ "embed" "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) //go:embed status_message_go.wasm var contractWasm []byte // @contract:state type Contract struct{} // @contract:payable min_deposit=1.1NEAR func (c *Contract) ExampleDeployContract(prefix string) { currentAccountId, _ := env.GetCurrentAccountId() subaccountId := prefix + "." + currentAccountId amount, _ := types.U128FromString("1100000000000000000000000") // 1.1Ⓝ promise.CreateBatch(subaccountId). CreateAccount(). Transfer(amount). DeployContract(contractWasm) } ``` :::tip If an account with a contract deployed does **not** have any access keys, this is known as a locked contract. When the account is locked, it cannot sign transactions therefore, actions can **only** be performed from **within** the contract code. ::: --- ## Add Keys When you use actions to create a new account, the created account does not have any [access keys](../../protocol/access-keys.md), meaning that it **cannot sign transactions** (e.g. to update its contract, delete itself, transfer money). There are two options for adding keys to the account: 1. `add_access_key`: adds a key that can only call specific methods on a specified contract. 2. `add_full_access_key`: adds a key that has full access to the account.
```rust use near_sdk::{near, env, Promise, NearToken, PublicKey}; #[near(serializers = [json, borsh])] #[derive(Default)] pub struct Contract { } const MIN_STORAGE: NearToken = NearToken::from_millinear(1100); //1.1Ⓝ const HELLO_CODE: &[u8] = include_bytes!("./hello.wasm"); #[near] impl Contract { pub fn create_hello(&self, prefix: String, public_key: PublicKey){ let account_id = prefix + "." + &env::current_account_id().to_string(); Promise::new(account_id.parse().unwrap()) .create_account() .transfer(MIN_STORAGE) .deploy_contract(HELLO_CODE.to_vec()) .add_full_access_key(public_key); } } ``` ```js import { NearBindgen, near, call, NearPromise } from 'near-sdk-js' import { PublicKey } from 'near-sdk-js/lib/types' const MIN_STORAGE: bigint = BigInt("1000000000000000000000") // 0.001Ⓝ @NearBindgen({}) class Contract { @call({}) create_hello({prefix, public_key}:{prefix: String, public_key: PublicKey}) { const account_id = `${prefix}.${near.currentAccountId()}` NearPromise.new(account_id) .createAccount() .transfer(MIN_STORAGE) .addFullAccessKey(public_key) } } ``` ```python from near_sdk_py import call, Contract from near_sdk_py.promises import Promise from near_sdk_py.constants import ONE_NEAR # Minimum amount needed for storage MIN_STORAGE = ONE_NEAR // 1000 # 0.001Ⓝ class ExampleContract(Contract): @call def create_with_key(self, prefix, public_key): """Create a subaccount with a full access key""" # Generate the new account ID account_id = f"{prefix}.{self.current_account_id}" # Create the new account, transfer some NEAR, and add a key return Promise.create_batch(account_id)\ .create_account()\ .transfer(MIN_STORAGE)\ .add_full_access_key(public_key) ``` ```go package main "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type AddKeysInput struct { Prefix string `json:"prefix"` PublicKey string `json:"public_key"` } // @contract:payable min_deposit=0.001NEAR func (c *Contract) ExampleAddKeys(input AddKeysInput) { currentAccountId, _ := env.GetCurrentAccountId() subaccountId := input.Prefix + "." + currentAccountId amount, _ := types.U128FromString("1000000000000000000000") // 0.001Ⓝ promise.CreateBatch(subaccountId). CreateAccount(). Transfer(amount). AddFullAccessKey([]byte(input.PublicKey), 0) } ``` Notice that what you actually add is a "public key". Whoever holds its private counterpart, i.e. the private-key, will be able to use the newly access key. :::tip If an account with a contract deployed does **not** have any access keys, this is known as a locked contract. When the account is locked, it cannot sign transactions therefore, actions can **only** be performed from **within** the contract code. ::: --- ## Delete Account There are two scenarios in which you can use the `delete_account` action: 1. As the **last** action in a chain of batched actions. 2. To make your smart contract delete its own account. ```rust use near_sdk::{near, env, Promise, NearToken, AccountId}; #[near(contract_state)] #[derive(Default)] pub struct Contract { } const MIN_STORAGE: NearToken = NearToken::from_millinear(1); //0.001Ⓝ #[near] impl Contract { pub fn create_delete(&self, prefix: String, beneficiary: AccountId){ let account_id = prefix + "." + &env::current_account_id().to_string(); Promise::new(account_id.parse().unwrap()) .create_account() .transfer(MIN_STORAGE) .delete_account(beneficiary); } pub fn self_delete(beneficiary: AccountId){ Promise::new(env::current_account_id()) .delete_account(beneficiary); } } ``` ```js import { NearBindgen, near, call, NearPromise } from 'near-sdk-js' import { AccountId } from 'near-sdk-js/lib/types' const MIN_STORAGE: bigint = BigInt("1000000000000000000000") // 0.001Ⓝ @NearBindgen({}) class Contract { @call({}) create_delete({prefix, beneficiary}:{prefix: String, beneficiary: AccountId}) { const account_id = `${prefix}.${near.currentAccountId()}` NearPromise.new(account_id) .createAccount() .transfer(MIN_STORAGE) .deleteAccount(beneficiary) } @call({}) self_delete({beneficiary}:{beneficiary: AccountId}) { NearPromise.new(near.currentAccountId()) .deleteAccount(beneficiary) } } ``` ```python from near_sdk_py import call, Contract from near_sdk_py.promises import Promise from near_sdk_py.constants import ONE_NEAR # Minimum amount needed for storage MIN_STORAGE = ONE_NEAR // 1000 # 0.001Ⓝ class ExampleContract(Contract): @call def create_delete(self, prefix, beneficiary): """Create an account and immediately delete it, sending funds to a beneficiary""" # Generate the new account ID account_id = f"{prefix}.{self.current_account_id}" # Create the account, transfer funds, then delete it return Promise.create_batch(account_id)\ .create_account()\ .transfer(MIN_STORAGE)\ .delete_account(beneficiary) @call def self_delete(self, beneficiary): """Delete this contract's account""" # Delete the account and send remaining funds to beneficiary return Promise.create_batch(self.current_account_id)\ .delete_account(beneficiary) ``` ```go package main "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type DeleteAccountInput struct { Prefix string `json:"prefix"` Beneficiary string `json:"beneficiary"` } type SelfDeleteInput struct { Beneficiary string `json:"beneficiary"` } // @contract:payable min_deposit=0.001NEAR func (c *Contract) ExampleCreateDeleteAccount(input DeleteAccountInput) { currentAccountId, _ := env.GetCurrentAccountId() subaccountId := input.Prefix + "." + currentAccountId amount, _ := types.U128FromString("1000000000000000000000") // 0.001Ⓝ promise.CreateBatch(subaccountId). CreateAccount(). Transfer(amount). DeleteAccount(input.Beneficiary) } // @contract:mutating func (c *Contract) ExampleSelfDeleteAccount(input SelfDeleteInput) { currentAccountId, _ := env.GetCurrentAccountId() promise.CreateBatch(currentAccountId). DeleteAccount(input.Beneficiary) } ``` :::warning Token Loss If the beneficiary account does not exist the funds will be [**dispersed among validators**](../../protocol/network/token-loss.md). ::: :::warning Token Loss Do **not** use `delete` to try fund a new account. Since the account doesn't exist the tokens will be lost. ::: --- # Source: https://docs.near.org/tutorials/examples/advanced-xcc.md --- id: advanced-xcc title: Complex Cross Contract Call description: "Batching, parallel actions, and callback handling." --- This example presents 3 instances of complex cross-contract calls on the NEAR blockchain, showcasing how to batch multiple function calls to a same contract, call multiple contracts in parallel, and handle responses in the callback. It includes both the smart contract and the frontend components. :::info Simple Cross-Contract Calls Check the tutorial on how to use [simple cross-contract calls](xcc.md) ::: --- ## Obtaining the Cross Contract Call 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/cross-contract-calls?quickstart=1) | 🌐 `https://github.com/near-examples/cross-contract-calls` | --- ## Structure of the Example The smart contract is available in two flavors: Rust and JavaScript ```bash ┌── sandbox-ts # sandbox testing │ ├── external-contracts │ │ ├── counter.wasm │ │ ├── guest-book.wasm │ │ └── hello-near.wasm │ └── main.ava.ts ├── src # contract's code │ ├── internal │ │ ├── batch_actions.ts │ │ ├── constants.ts │ │ ├── multiple_contracts.ts │ │ ├── similar_contracts.ts │ │ └── utils.ts │ └── contract.ts ├── package.json ├── README.md └── tsconfig.json ``` ```bash ┌── tests # sandbox testing │ ├── external-contracts │ │ ├── counter.wasm │ │ ├── guest-book.wasm │ │ └── hello-near.wasm │ └── main.ava.ts ├── src # contract's code │ ├── batch_actions.rs │ ├── lib.rs │ ├── multiple_contracts.rs │ └── similar_contracts.rs ├── Cargo.toml # package manager ├── README.md └── rust-toolchain.toml ``` --- ## Smart Contract ### Batch Actions You can aggregate multiple actions directed towards one same contract into a batched transaction. Methods called this way are executed sequentially, with the added benefit that, if one fails then they **all get reverted**. ``` @call({}) batch_actions(): NearPromise { return internal_batch_actions(this.hello_account); } ``` ``` export function batch_actions(accountId: AccountId) { const promise = NearPromise.new(accountId) .functionCall("set_greeting", JSON.stringify({ greeting: 'hi' }), NO_DEPOSIT, TEN_TGAS) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS) .functionCall("set_greeting", JSON.stringify({ greeting: 'bye' }), NO_DEPOSIT, TEN_TGAS) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS) .then( NearPromise.new(near.currentAccountId()) .functionCall("batch_actions_callback", NO_ARGS, NO_DEPOSIT, TEN_TGAS) ) return promise.asReturn(); }; ``` ``` pub fn batch_actions(&mut self) -> Promise { let hi = json!({ "greeting": "hi" }).to_string().into_bytes(); let bye = json!({ "greeting": "bye" }).to_string().into_bytes(); // You can create one transaction calling multiple methods // on a same contract Promise::new(self.hello_account.clone()) .function_call("set_greeting".to_owned(), hi, NO_DEPOSIT, XCC_GAS) .function_call("get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS) .function_call("set_greeting".to_owned(), bye, NO_DEPOSIT, XCC_GAS) .function_call("get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS) .then(Self::ext(env::current_account_id()).batch_actions_callback()) } ``` #### Getting the Last Response In this case, the callback has access to the value returned by the **last action** from the chain. ``` @call({ privateFunction: true }) batch_actions_callback(): string { return internal_batch_actions_callback(); } ``` ``` export function batch_actions_callback() { let { success, result } = promiseResult(0); if (success) { near.log(`Success! Result: ${result}`); return result; } else { near.log("Promise failed..."); return ""; } }; ``` ``` export function promiseResult(index: number): { result: string, success: boolean } { let result, success; try { result = near.promiseResult(index); success = true; } catch (err){ near.log(err.message); throw err; result = undefined; success = false; } return { result, success, } } ``` ``` #[private] pub fn batch_actions_callback( &self, #[callback_result] last_result: Result, ) -> String { // The callback only has access to the last action's result if let Ok(result) = last_result { log!("The last result is {result}"); result } else { log!("The batch call failed and all calls got reverted"); "".to_string() } } } ``` --- ### Calling Multiple Contracts A contract can call multiple other contracts. This creates multiple transactions that execute all in parallel. If one of them fails the rest **ARE NOT REVERTED**. ``` @call({}) multiple_contracts(): NearPromise { return internal_multiple_contracts(this); } ``` ``` export function multiple_contracts(contract: CrossContractCall) { const promise1 = NearPromise.new(contract.hello_account) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS) const promise2 = NearPromise.new(contract.counter_account) .functionCall("get_num", NO_ARGS, NO_DEPOSIT, TEN_TGAS) const promise3 = NearPromise.new(contract.guestbook_account) .functionCall("get_messages", NO_ARGS, NO_DEPOSIT, TEN_TGAS) return promise1 .and(promise2) .and(promise3) .then( NearPromise.new(near.currentAccountId()) .functionCall("multiple_contracts_callback", JSON.stringify({ number_promises: 3 }), NO_DEPOSIT, TEN_TGAS) ) }; ``` ``` /// A method which calls different contracts via cross contract function calls. pub fn multiple_contracts(&mut self) -> Promise { // We create a promise that calls the `get_greeting` function on the HELLO_CONTRACT let hello_promise = Promise::new(self.hello_account.clone()).function_call( "get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS, ); // We create a promise that calls the `get_num` function on the COUNTER_CONTRACT let counter_promise = Promise::new(self.counter_account.clone()).function_call( "get_num".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS, ); // We create a promise that calls the `get_messages` function on the GUESTBOOK_CONTRACT let args = json!({ "from_index": "0", "limit": 2 }) .to_string() .into_bytes(); let guestbook_promise = Promise::new(self.guestbook_account.clone()).function_call( "get_messages".to_owned(), args, NO_DEPOSIT, XCC_GAS, ); // We join all promises and chain a callback to collect their results. hello_promise .and(counter_promise) .and(guestbook_promise) .then( Self::ext(env::current_account_id()) .with_static_gas(XCC_GAS) .multiple_contracts_callback(), ) } ``` #### Getting All Responses In this case, the callback has access to an **array of responses**, which have either the value returned by each call, or an error message. ``` @call({ privateFunction: true }) multiple_contracts_callback( { number_promises }: { number_promises: number }, ): string[] { return internal_multiple_contracts_callback(number_promises); } ``` ``` export function multiple_contracts_callback(number_promises: number): string[] { const allResults = []; for (let i = 0; i < number_promises; i++) { near.log(`Get index result: ${i}`); let { success, result } = promiseResult(i); if (success) { allResults.push(result); near.log(`Success! Index: ${i}, Result: ${result}`); } else { near.log("Promise failed..."); return []; } } return allResults; }; ``` ``` export function promiseResult(index: number): { result: string, success: boolean } { let result, success; try { result = near.promiseResult(index); success = true; } catch (err){ near.log(err.message); throw err; result = undefined; success = false; } return { result, success, } } ``` ``` pub fn multiple_contracts_callback( &self, #[callback_result] hello_result: Result, #[callback_result] counter_result: Result, #[callback_result] guestbook_result: Result, PromiseError>, ) -> (String, i8, Vec) { // The callback has access to the result of the 3 calls let greeting = if let Ok(result) = hello_result { log!("HelloNear says {result}"); result } else { log!("The call to HelloNear failed"); "".to_string() }; let counter = if let Ok(result) = counter_result { log!("Counter is {result}"); result } else { log!("The call to Counter failed"); 0 }; let messages = if let Ok(result) = guestbook_result { log!("The messages are {result:?}"); result } else { log!("The call to GuestBook failed"); vec![] }; (greeting, counter, messages) } } ``` --- ### Multiple Calls - Same Result Type This example is a particular case of the previous one ([Calling Multiple Contracts](#calling-multiple-contracts)). It simply showcases a different way to check the results by directly accessing the `promise_result` array. In this case, we call multiple contracts that will return the same type: ``` @call({ privateFunction: true }) similar_contracts_callback( { number_promises }: { number_promises: number }, ): string[] { return internal_similar_contracts_callback(number_promises); } } ``` ``` function promise_set_get(contract: CrossContractCall, message: string) { return NearPromise.new(contract.hello_account) .functionCall( "set_greeting", JSON.stringify({ greeting: message }), NO_DEPOSIT, TEN_TGAS ) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS); } export function similar_contracts(contract: CrossContractCall) { const hello_one = promise_set_get(contract,"hi"); const hello_two = promise_set_get(contract,"howdy"); const hello_three = promise_set_get(contract,"bye"); return hello_one .and(hello_two) .and(hello_three) .then( NearPromise.new(near.currentAccountId()).functionCall( "multiple_contracts_callback", JSON.stringify({ number_promises: 3 }), NO_DEPOSIT, TEN_TGAS ) ); } ``` ``` fn promise_set_get(&self, message: &str) -> Promise { // Aux method to create a batch transaction calling // set_message and get_message in the HELLO CONTRACT let args = json!({ "greeting": message }).to_string().into_bytes(); Promise::new(self.hello_account.clone()) .function_call("set_greeting".to_owned(), args, NO_DEPOSIT, XCC_GAS) .function_call("get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS) } pub fn similar_contracts(&mut self) -> Promise { // Create promises to call 3 contracts that return the same type // For simplicity here we call the same contract let hello_one = self.promise_set_get("hi"); let hello_two = self.promise_set_get("howdy"); let hello_three = self.promise_set_get("bye"); // Join all promises and chain a callback to collect their results. hello_one.and(hello_two).and(hello_three).then( Self::ext(env::current_account_id()) .with_static_gas(XCC_GAS) .similar_contracts_callback(3), ) } ``` #### Getting All Responses In this case, the callback again has access to an **array of responses**, which we can iterate checking the results. ``` return internal_similar_contracts(this); } @call({ privateFunction: true }) similar_contracts_callback( ``` ``` export function similar_contracts_callback(number_promises: number): string[] { const allResults = []; for (let i = 0; i < number_promises; i++) { near.log(`Get index result: ${i}`); let { success, result } = promiseResult(i); if (success) { allResults.push(result); near.log(`Success! Index: ${i}, Result: ${result}`); } else { near.log("Promise failed..."); return []; } } return allResults; }; ``` ``` export function promiseResult(index: number): { result: string, success: boolean } { let result, success; try { result = near.promiseResult(index); success = true; } catch (err){ near.log(err.message); throw err; result = undefined; success = false; } return { result, success, } } ``` ``` #[private] pub fn similar_contracts_callback(&self, number_promises: u64) -> Vec { (0..number_promises) .filter_map(|index| { // env::promise_result(i) has the result of the i-th call let result = env::promise_result(index); match result { PromiseResult::Failed => { log!("Promise number {index} failed."); None } PromiseResult::Successful(value) => { if let Ok(message) = near_sdk::serde_json::from_slice::(&value) { log!("Call {index} returned: {message}"); Some(message) } else { log!("Error deserializing call {index} result."); None } } } }) .collect() } } ``` --- ### 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-advanced-ts yarn yarn test ``` ```bash cd contract-advanced-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-advanced-ts` or `cd contract-advanced-rs`), build and deploy it: ```bash npm run build near deploy ./build/cross_contract.wasm --initFunction new --initArgs '{"hello_account":"hello.near-example.testnet","guestbook_account":"guestbook_account.near-example.testnet","counter_account":"counter_account.near-example.testnet"}' ``` ```bash cargo near deploy build-non-reproducible-wasm with-init-call new json-args '{"hello_account":"hello.near-example.testnet","guestbook_account":"guestbook_account.near-example.testnet","counter_account":"counter_account.near-example.testnet"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send ```
### CLI: Interacting with the Contract To interact with the contract through the console, you can use the following commands: ```bash # Execute contracts sequentially # Replace with your account ID near call batch_actions --useAccount --gas 300000000000000 # Execute contracts in parallel # Replace with your account ID near call multiple_contracts --useAccount --gas 300000000000000 # Execute multiple instances of the same contract in parallel # Replace with your account ID near call similar_contracts --useAccount --gas 300000000000000 ``` ```bash # Execute contracts sequentially # Replace with your account ID near contract call-function as-transaction batch_actions json-args '{}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send # Execute contracts in parallel # Replace with your account ID near contract call-function as-transaction multiple_contracts json-args '{}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send # Execute multiple instances of the same contract in parallel # Replace with your account ID near contract call-function as-transaction similar_contracts json-args '{}' prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send ``` :::info If at some point you get an "Exceeded the prepaid gas" error, try to increase the gas amount used within the functions when calling other contracts ::: :::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` ::: --- # Source: https://docs.near.org/smart-contracts/anatomy/anatomy.md --- id: anatomy title: Basic Anatomy hide_table_of_contents: true description: "Learn the basic anatomy of all smart contracts." --- Let's illustrate the basic anatomy of a simple "Hello World" contract. The code on this page comes from our [Hello NEAR repository](https://github.com/near-examples/hello-near-examples) on GitHub. ### Importing the SDK All contracts will import the **NEAR SDK**, enabling them to [access the execution environment](./environment.md), [call other contracts](./crosscontract.md), [transfer tokens](./actions.md), and much more. You can also use third-party libraries, though some might not work due to the limitations of the contract runtime. ### Contract's Main Structure The contract is described through a structure: - The attributes define which data the contract stores - The functions define its public (and private) interface ### Comment Directives Unlike languages with built-in decorators, the Near Go SDK uses **Comment Directives** to control how your code is compiled into a smart contract. The generator (built into the `near-go` CLI) scans your comments for specific tags starting with `@contract:`. > **Note:** These directives rely on the `near-go` build process and will not work if you compile using raw `tinygo`. 1. **`@contract:state`**: Placed above a `struct`. It identifies the main state of the smart contract. **Only one state struct is allowed per project.** The CLI automatically generates `getState` and `setState` helper methods. 2. **`@contract:init`**: Marks the initialization method (constructor). It checks if the state is empty before running. **Only one init method is allowed per project.** 3. **`@contract:view`**: Marks a method as read-only. It exposes the method to the outside world but does not save changes to the state. Compatible with `@contract:promise_callback`. 4. **`@contract:mutating`**: Marks a method that modifies the state. The CLI automatically saves the updated state back to storage after execution. Required for callbacks that need to modify data. 5. **`@contract:payable`**: Allows the method to accept attached NEAR tokens. You can optionally specify requirements like `min_deposit=1NEAR`. Compatible with `mutating` and `init`. 6. **`@contract:promise_callback`**: Handles the result of a cross-contract call by injecting `promise.PromiseResult` into the arguments. **Must be combined** with either `@contract:view` (for read-only logic) or `@contract:mutating` (to save state changes). **Note:** Methods exported to WASM will automatically be converted to `snake_case` (e.g., `SetGreeting` becomes `set_greeting`). ### Contract Class Decorator Note that the contract's class is decorated with `@NearBindgen`. This decorator tells the SDK which class defines the contract, so it knows: 1. What to fetch from storage when the contract is loaded 2. What to store when the contract is done executing 3. The methods that are exposed to the outside world 4. If the contract needs to be initialized (we will cover this later) **Note:** Only one class can be decorated with the `@NearBindgen` decorator ### Python Class Structure In Python, we use a class to define our contract. Unlike JavaScript or Rust, there isn't a specific decorator for the class itself. Instead, each method that should be exposed to the blockchain is decorated with the appropriate decorator (`@view`, `@call`, or `@init`). The contract's state is managed through instance variables and can be persisted using the Storage API or collections. ### Contract Struct Macro Note that the contract's struct definition and the implementation are decorated with macros The `#[near(contract_state)]` macro tells the SDK that this structure defines the contract's state, so it knows: 1. What to fetch from storage when the contract is loaded 2. What to store when the contract is done executing The `#[near]` macro tells the SDK which functions are exposed to the outside world. **Note:** Only one struct can be decorated with the `#[near(contract_state)]` macro.
Interaction with other macros When `near` is built for the wasm32 target, it generates the external NEAR contract bindings. To achieve this it is actually generating another function with the signature `pub extern "C" fn function_name()` that first deserializes the contract struct from NEAR storage and then calls the `contract.function_name(parameter1, parameter2, ...)`. If you have annotated your function with any attribute-like macros, these are then executed _twice_. Specifically if the attribute like macro makes any modification to the function signature, or inserts any code that depends on the contract struct (in the form of `&self`, `&mut self`, or `self`) this will fail in the second invocation, because the externally exposed function does not have any concept of this struct. It is possible to detect this by checking which build target you are building for and limit the execution of the macro to operate only on the first pass.
### Storage (State) We call the data stored in the contract [the contract's state](./storage.md). In our Hello World example, the contract stores a single string (`greeting`), and the state starts initialized with the default value `"Hello"`. **Note:** We will cover more about the contract's state in the [state section](./storage.md). Javascript contracts need to further include a `schema` object that defines the contract's state and its types. This object is used by the SDK to correctly serialize and deserialize the contract's state. ### Method Decorators In Python, contract methods are decorated with `@view`, `@call`, or `@init` to define how they can be accessed. These decorators handle input parsing and serializing return values automatically. ### Read Only Functions Contract's functions can be read-only, meaning they don't modify the state. Calling them is free for everyone, and does not require to have a NEAR account. **Note:** We will cover more about function types in the [functions section](./functions.md). ### State Mutating Functions Functions that modify the state or call other contracts are considered state mutating functions. It is necessary to have a NEAR account to call them, as they require a transaction to be sent to the network. **Note:** We will cover more about function types in the [functions section](./functions.md). ```js import { NearBindgen, near, call, view } from 'near-sdk-js'; @NearBindgen({}) class HelloNear { greeting: string = 'Hello'; static schema = { // JS contracts need a schema "greeting": "string" }; @view({}) // This method is read-only and can be called for free get_greeting(): string { return this.greeting; } @call({}) // This method changes the state, for which it cost gas set_greeting({ greeting }: { greeting: string }): void { near.log(`Saving greeting ${greeting}`); this.greeting = greeting; } } ``` ```rust use near_sdk::{log, near}; // Define the contract structure #[near(contract_state)] pub struct Contract { greeting: String, } // Define the default, which automatically initializes the contract impl Default for Contract { fn default() -> Self { Self { greeting: "Hello".to_string(), } } } // Implement the contract structure #[near] impl Contract { // Public method - returns the greeting saved, defaulting to DEFAULT_GREETING pub fn get_greeting(&self) -> String { self.greeting.clone() } // Public method - accepts a greeting, such as "howdy", and records it pub fn set_greeting(&mut self, greeting: String) { log!("Saving greeting: {}", greeting); self.greeting = greeting; } } ``` ```python from near_sdk_py import view, call, Contract # Define contract class class HelloNear: @init def new(self): # Initialize state with default greeting self.storage["greeting"] = "Hello" @view def get_greeting(self) -> str: """Returns the current greeting""" return self.storage["greeting"] @call def set_greeting(self, message: str) -> str: """Sets a new greeting""" self.storage["greeting"] = message return message ``` ```go package main import ( "github.com/vlmoon99/near-sdk-go/collections" "github.com/vlmoon99/near-sdk-go/env" ) // @contract:state type GreetingContract struct { Greetings *collections.UnorderedMap[string, string] } // @contract:init func (c *GreetingContract) Init() { c.Greetings = collections.NewUnorderedMap[string, string]("g") env.LogString("Hello from Init Method") c.Greetings.Insert("default", "Hello from NEAR!") } // @contract:view func (c *GreetingContract) GetGreeting() string { val, err := c.Greetings.Get("default") if err != nil { return "Default greeting not found" } return val } // @contract:mutating func (c *GreetingContract) SetGreeting(greeting string) { c.Greetings.Insert("default", greeting) } ```
--- # Source: https://docs.near.org/ai/shade-agents/reference/api.md --- id: api title: Shade Agent API sidebar_label: Shade Agent API description: "Learn how to use the Shade Agent API." --- The Shade Agent API abstracts away the complexity of the TEE and interacting with the agent contract to help you build a Shade Agent quickly. --- ## API Overview The API is packaged as a Docker image and included in your agent when it's uploaded to Phala Cloud. The API is accessible internally by default on port 3140, but it's not accessible from outside the TEE. When the API image boots up, it will automatically derive the agent's NEAR account (a random [implicit account](https://near-docs-pr-2740.onrender.com/protocol/account-id#implicit-address)), fund it with 0.3 NEAR from the NEAR_ACCOUNT_ID specified in the environment variables, and registers the agent in the agent contract. The API can be used in any language, but API wrappers are maintained in TypeScript/JavaScript and Python. It's recommended you develop in TypeScript as it has great synergies with [chainsig.js](../../../chain-abstraction/chain-signatures/implementation.md) for building multichain transactions. --- ## Setup ```bash npm install @neardefi/shade-agent-js ``` ```bash pip install shade-agent-py ``` You can use the Shade Agent API directly with any language that supports HTTP requests. The base API endpoints change depending on your deployment: **Local Development:** ``` http://localhost:3140/api/agent ``` **Production (TEE):** ``` http://shade-agent-api:3140/api/agent ``` All endpoints expect `POST` requests with JSON payloads and return JSON responses. If you're running your API on a port other than 3140, you should amend the base URL accordingly. --- ## Agent Account ID Fetches the NEAR account ID of the agent. ```ts import { agentAccountId } from '@neardefi/shade-agent-js'; const res = await agentAccountId(); const accountId = res.accountId ``` ```py from shade_agent import agent_account_id res = await agent_account_id() account_id = res["account_id"] ``` **POST /getAccountId** Request body ```json {} ``` Response ```json { "accountId": "14bc8ee18f2d1ef51e7d581bdd96797804c56247733defdae67ad41314686fd7" } ``` --- ## Agent Info Fetches the code hash and checksum for the agent. - The `code hash` is the code hash of the app image running inside the agent. - The `checksum` is produced by the TEEs attestation and represents that the agent is registered. This function will only return the details once the agent has successfully registered in the agent contract. When running the API locally, it will only return the code hash, and not the checksum. ```ts import { agentInfo } from '@neardefi/shade-agent-js'; const res = await agentInfo(); const codehash = res.codehash const checksum = res.checksum ``` ```py from shade_agent import agent_info res = await agent_info() codehash = res["codehash"] checksum = res["checksum"] ``` **POST /view** Request body ```json { "methodName": "get_agent", "args": { "account_id": "14bc8ee18f2d1ef51e7d581bdd96797804c56247733defdae67ad41314686fd7" } } ``` Response ```json { "codehash": "03bcd36d3ffb5346c9e1e0166a4c2734a9e7cceedee6f7d992499aeb7fa54ead", "checksum": "0c32c5c598bc8a3bfec47f3d7d9a9d600c8a60e5b97d90a4c2856a7c829eb6d4" } ``` --- ## Agent Balance Fetches the NEAR balance of the agent's account in yoctoNEAR (1 NEAR = 10^24 yoctoNEAR). ```ts import { agent } from '@neardefi/shade-agent-js'; const res = await agent("getBalance"); const balance = res.balance ``` ```py from shade_agent import agent res = await agent("getBalance") balance = res["balance"] ``` **POST /getBalance** Request body ```json {} ``` Response ```json { "balance": "203436730084671139765003" } ``` --- ## Request Signature Requests a signature from the Shade Agent for a multichain account (by calling request_signature on the agent contract). It has three arguments: - **path** - A string that decides which account the signature is for. The path can be set to anything, and by changing the path, you produce a signature for a different account. - **payload** - The hash of the transaction to be signed, given as a hex string. - **keyType** - The signature scheme being used to sign the payload `Ecdsa` (secp256k1) or `Eddsa` (ed25519). It returns the signature for the transaction. ```ts import { requestSignature } from '@neardefi/shade-agent-js'; const res = await requestSignature({ path: "ethereum-1", payload: "cf80cd8a...", keyType: "Ecdsa", // Or "Eddsa" }); ```
Response For `Ecdsa`, the function returns the components of the signature as hex strings. Note that to get `r`, remove the first two hex characters from `big_r`. ```typescript { scheme: 'Secp256k1', big_r: { affine_point: '03D537AFFD52BE9AF0DA6CF41B573F4BE065434AEE2D25A500BC730C06E7EB2AF1' }, s: { scalar: '3470037EB46DC6D1921900B635785290184EC980CFEC7109EB103B5698D4F725' }, recovery_id: 0 } ``` For `Eddsa`, the function returns the whole signature as a 64-byte array. ```typescript { scheme: 'Ed25519', signature: [ 5, 105, 30, 208, 192, 39, 154, 105, 252, 20, 132, 64, 247, 207, 223, 127, 197, 43, 30, 145, 164, 224, 1, 45, 240, 28, 155, 218, 204, 5, 136, 111, 238, 40, 120, 122, 249, 166, 193, 174, 120, 94, 177, 39, 179, 193, 170, 117, 37, 36, 155, 38, 72, 24, 118, 235, 187, 110, 129, 26, 186, 7, 0, 8 ] } ``` If you're using the chainsig.js library, you don't need to worry about the format of these responses since the library handles it.
```py from shade_agent import request_signature res = await request_signature( path="ethereum-1", payload="cf80cd8a...", key_type="Ecdsa", # Or "Eddsa" ) ```
Response For `Ecdsa`, the function returns the components of the signature as hex strings. Note that to get `r`, remove the first two hex characters from `big_r`. ```python { 'scheme': 'Secp256k1', 'big_r': { 'affine_point': '03D537AFFD52BE9AF0DA6CF41B573F4BE065434AEE2D25A500BC730C06E7EB2AF1' }, 's': { 'scalar': '3470037EB46DC6D1921900B635785290184EC980CFEC7109EB103B5698D4F725' }, 'recovery_id': 0 } ``` For `Eddsa`, the function returns the whole signature as a 64-byte array. ```python { 'scheme': 'Ed25519', 'signature': [ 5, 105, 30, 208, 192, 39, 154, 105, 252, 20, 132, 64, 247, 207, 223, 127, 197, 43, 30, 145, 164, 224, 1, 45, 240, 28, 155, 218, 204, 5, 136, 111, 238, 40, 120, 122, 249, 166, 193, 174, 120, 94, 177, 39, 179, 193, 170, 117, 37, 36, 155, 38, 72, 24, 118, 235, 187, 110, 129, 26, 186, 7, 0, 8 ] } ```
**POST /call** Request body ```json { "methodName": "request_signature", "args": { "path": "ethereum-1", "payload": "cf80cd8a...", "key_type": "Ecdsa" } } ```
Response For `Ecdsa`, the function returns the components of the signature as hex strings. Note that to get `r`, remove the first two hex characters from `big_r`. ```json { "scheme": "Secp256k1", "big_r": { "affine_point": "03D537AFFD52BE9AF0DA6CF41B573F4BE065434AEE2D25A500BC730C06E7EB2AF1" }, "s": { "scalar": "3470037EB46DC6D1921900B635785290184EC980CFEC7109EB103B5698D4F725" }, "recovery_id": 0 } ``` For `Eddsa`, the function returns the whole signature as a 64-byte array. ```json { "scheme": "Ed25519", "signature": [ 5, 105, 30, 208, 192, 39, 154, 105, 252, 20, 132, 64, 247, 207, 223, 127, 197, 43, 30, 145, 164, 224, 1, 45, 240, 28, 155, 218, 204, 5, 136, 111, 238, 40, 120, 122, 249, 166, 193, 174, 120, 94, 177, 39, 179, 193, 170, 117, 37, 36, 155, 38, 72, 24, 118, 235, 187, 110, 129, 26, 186, 7, 0, 8 ] } ```
--- ## Agent Call Makes a function call to the agent contract from the agent. This is used for custom contracts when you want to call a function other than request_signature. It returns the result of the function call. ```ts import { agentCall } from '@neardefi/shade-agent-js'; const res = await agentCall({ methodName: "example_call_method", args: { arg1: "Value1", arg2: "Value2", }, gas: "30000000000000", // Optional }) ``` ```py from shade_agent import agent_call res = await agent_call({ "methodName": "example_call_method", "args": { "arg1": "Value1", "arg2": "Value2", }, "gas": "30000000000000", # Optional }) ``` **POST /call** Request body ```json { "methodName": "example_call_method", "args": { "arg1": "value1", "arg2": "value2" }, "gas": "30000000000000" // Optional } ``` --- ## Agent View Makes a function call to a view method (a method that does not require gas) on the agent contract. It returns the result of the function call. ```ts import { agentView } from '@neardefi/shade-agent-js'; const res = await agentView({ methodName: "example_view_method", args: { arg1: "value1", arg2: "value2", }, }) ``` ```py from shade_agent import agent_view res = await agent_view({ "methodName": "example_view_method", "args": { "arg1": "value1", "arg2": "value2", }, }) ``` **POST /view** Request body ```json { "methodName": "example_view_method", "args": { "arg1": "value1", "arg2": "value2" } } ``` --- # Source: https://docs.near.org/protocol/architecture.md --- id: architecture title: Architecture description: "A comprehensive high-level overview of NEAR Protocol's architecture" --- NEAR consists roughly of a blockchain layer and a [runtime layer](network/runtime.md). These layers are designed to be independent from each other: the blockchain layer can in theory support runtime that processes transactions differently, has a different virtual machine (e.g. RISC-V), has different fees; on the other hand the runtime is oblivious to where the transactions are coming from. It is not aware whether the blockchain it runs on is sharded, what consensus it uses, and whether it runs as part of a blockchain at all. The blockchain layer and the runtime layer share the following components and invariants: ## Transactions and Receipts [Transactions](transactions.md) and receipts are a fundamental concept in Near Protocol. Transactions represent actions requested by the blockchain user, e.g. send assets, create account, execute a method, etc. Receipts, on the other hand is an internal structure; think of a receipt as a message which is used inside a message-passing system. Transactions are created outside the Near Protocol node, by the user who sends them via RPC or network communication. Receipts are created by the runtime from transactions or as the result of processing other receipts. Blockchain layer cannot create or process transactions and receipts, it can only manipulate them by passing them around and feeding them to a runtime. ## Account-Based System Similar to Ethereum, Near Protocol is an [account-based system](account-model.md). Which means that each blockchain user is roughly associated with one or several accounts (there are exceptions though, when users share an account and are separated through the [access keys](access-keys.md)). The runtime is essentially a complex set of rules on what to do with accounts based on the information from the transactions and the receipts. It is therefore deeply aware of the concept of account. Blockchain layer however is mostly aware of the accounts through [the trie](#trie) and [the validators](#validators). Outside these two it does not operate on the accounts directly. ### Assume every account belongs to its own shard Every account at NEAR belongs to some shard. All the information related to this account also belongs to the same shard. The information includes: - Balance - Locked balance (for staking) - Code of the contract - Key-value storage of the contract - All Access Keys Runtime assumes, it's the only information that is available for the contract execution. While other accounts may belong to the same shards, the Runtime never uses or provides them during contract execution. We can just assume that every account belongs to its own shard. So there is no reason to intentionally try to collocate accounts. ## Trie Near Protocol is a stateful blockchain -- there is a state associated with each account and the user actions performed through transactions mutate that state. The state then is stored as a trie, and both the blockchain layer and the runtime layer are aware of this technical detail. The blockchain layer manipulates the trie directly. It partitions the trie between the shards to distribute the load. It synchronizes the trie between the nodes, and eventually it is responsible for maintaining the consistency of the trie between the nodes through its consensus mechanism and other game-theoretic methods. The runtime layer is also aware that the storage that it uses to perform the operations on is a trie. In general it does not have to know this technical detail and in theory we could have abstracted out the trie as a generic key-value storage. However, we allow some trie-specific operations that we expose to the smart contract developers so that they utilize Near Protocol to its maximum efficiency. ## Tokens and gas Even though [tokens](network/tokens.md) is a fundamental concept of the blockchain, it is neatly encapsulated inside the runtime layer together with the [gas](gas.md), [fees](gas.md#understanding-gas-fees), and rewards. The only way the blockchain layer is aware of the tokens and the gas is through the computation of the exchange rate and the inflation which is based strictly on the block production mechanics. ## Validators Both the blockchain layer and the runtime layer are aware of a special group of participants who are responsible for maintaining the integrity of the Near Protocol. These participants called [Validators](network/validators.md) are associated with the accounts and are rewarded accordingly. The reward part is what the runtime layer is aware of, while everything around the orchestration of the validators is inside the blockchain layer. ## Blockchain Layer Concepts Interestingly, the following concepts are for the blockchain layer only and the runtime layer is not aware of them: - Sharding -- the runtime layer does not know that it is being used in a sharded blockchain, e.g. it does not know that the trie it works on is only a part of the overall blockchain state; - Blocks or chunks -- the runtime does not know that the receipts that it processes constitute a chunk and that the output receipts will be used in other chunks. From the runtime perspective it consumes and outputs batches of transactions and receipts; - Consensus -- the runtime does not know how consistency of the state is maintained; - Communication -- the runtime does not know anything about the current network topology. Receipt has only a `receiver_id` (a recipient account), but knows nothing about the destination shard, so it's a responsibility of the blockchain layer to route a particular receipt. ## Runtime Layer Concepts - [Fees and rewards](gas.md) -- fees and rewards are neatly encapsulated in the runtime layer. The blockchain layer, however, has an indirect knowledge of them through the computation of the tokens-to-gas exchange rate and the inflation. --- # Source: https://docs.near.org/tutorials/auction/auction-factory.md --- id: auction-factory title: Auction factory sidebar_label: Auction Factory description: "Create new auctions through a factory." --- Since an auction contract hosts a single auction, each time you would like to host a new auction you will need to deploy a new contract. Rather than finding the compiled WASM file, creating a new account, deploying the contract, and then initializing it each time, you can use a factory contract to do this for you. Luckily for us, there is already a [factory contract example](https://github.com/near-examples/factory-rust)! We will fork this example and slightly modify it to suit our use case. If you would like to learn more about how the factory contract works, you can take a look at the [associated documentation](/tutorials/examples/factory#generic-factory). The factory example only comes in rust since, currently, the JavaScript SDK does not allow you to embed the WASM file in the contract. This is a limitation of the SDK and not the blockchain itself. --- ## Changing the default contract In the current example, the factory contract deploys the donation contract example. We will change this to deploy our auction contract instead. Firstly, we'll need the compiled auction contract WASM file. You can get this by running the following command in [03-bid-with-fts](https://github.com/near-examples/auctions-tutorial/tree/reorg-auction/contract-rs/03-bid-with-fts) of `contract-rs` ```bash cargo near build ``` You will find the resulting WASM file in `target/near`; copy this file and use it to replace the WASM of the donation contract in the factory contract's source folder. Now edit the auction contract changing the path to the auction contract. ``` impl Default for Contract { fn default() -> Self { Self { code: LazyOption::new("code".as_bytes(), Some(AUCTION_CONTRACT.to_vec())), } } } ``` ``` const AUCTION_CONTRACT: &[u8] = include_bytes!("./auction-contract/auction.wasm"); const TGAS: Gas = Gas::from_tgas(1); ``` On initialization, the factory will add the auction contracts WASM, as bytes, to the factory's state. It is more efficient to not store the WASM in the factory's state, however, we may want to update the auction contract if we find a bug or want to add new features. The factory implements a method to update the auction contract - we'll change the name to `update_auction_contract` as this factory will only deploy auction contracts. ``` pub fn update_auction_contract(&mut self) { // This method receives the code to be stored in the contract directly // from the contract's input. In this way, it avoids the overhead of // deserializing parameters, which would consume a huge amount of GAS self.code.set(env::input()); } ``` --- ## Modifying deploy method The method to deploy a new contract is specific to the contract being deployed (in the case the contract has custom initialization parameters). We will modify the method to take in the auction contract's initialization parameters. ``` #[derive(Serialize)] #[serde(crate = "near_sdk::serde")] struct AuctionInitArgs { end_time: U64, auctioneer: AccountId, ft_contract: AccountId, nft_contract: AccountId, token_id: TokenId, starting_price: U128, } #[near] impl Contract { #[payable] pub fn deploy_new_auction( &mut self, name: String, end_time: U64, auctioneer: AccountId, ft_contract: AccountId, nft_contract: AccountId, token_id: TokenId, starting_price: U128, ) -> Promise { // Assert the sub-account is valid let current_account = env::current_account_id().to_string(); let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); assert!( env::is_valid_account_id(subaccount.as_bytes()), "Invalid subaccount" ); // Assert enough tokens are attached to create the account and deploy the contract let attached = env::attached_deposit(); let code = self.code.clone().unwrap(); let contract_bytes = code.len() as u128; let contract_storage_cost = NEAR_PER_STORAGE.saturating_mul(contract_bytes); let minimum_needed = contract_storage_cost.saturating_add(NearToken::from_millinear(100)); assert!( attached >= minimum_needed, "Attach at least {minimum_needed} yⓃ" ); let args = &AuctionInitArgs { end_time, auctioneer, ft_contract, nft_contract, token_id, starting_price, }; let init_args = near_sdk::serde_json::to_vec(args).unwrap(); let promise = Promise::new(subaccount.clone()) .create_account() .transfer(attached) .deploy_contract(code) .function_call( "init".to_owned(), init_args, NO_DEPOSIT, TGAS.saturating_mul(5), ); // Add callback promise.then( Self::ext(env::current_account_id()).deploy_new_auction_callback( subaccount, env::predecessor_account_id(), attached, ), ) } ``` In this fork, we have also removed the option to add an access key to the contract account since, as discussed [earlier](./1.3-deploy.md#locking-the-contract), we want auctions to be locked. --- ## Using the factory Build and deploy the factory like you would any other contract, this time without any initialization parameters. ```bash # compile the contract using cargo-near cargo near build # deploy the contract near deploy ./target/near/contract.wasm ``` You can now use the factory to deploy new auction contracts, here is an example command. ```bash near call auction-factory.testnet deploy_new_auction '{"name": "new-auction", "end_time": "3000000000000000000", "auctioneer": "pivortex.testnet", "ft_contract": "dai.fakes.testnet", "nft_contract": "nft.examples.testnet", "token_id": "7777", "starting_price": "1000000000000000000"}' --useAccount pivortex.testnet --deposit 1.6 --gas 100000000000000 ``` :::info Deposit and storage costs Note that we attach 1.6 $NEAR to the call to cover the storage costs of deploying the new auction. The storage cost on NEAR is 1 $NEAR per 100 kb, and our auction contract is around 140 kb, but we'll add a little to cover the storage used on initialization. ::: The command results in a fresh auction contract being deployed and initialized at `new-auction.auction-factory.testnet`. --- ## Conclusion In this part of the tutorial, you have learned how to fork and modify the factory contract example to deploy our auction contracts. You have also learned how to use the factory to deploy new auction contracts. If you're feeling adventurous you could create a frontend to interact with the factory contract to make it even easier to deploy new auctions. If you do so feel free to share it in our developer [Telegram](https://t.me/neardev) or [Discord](https://discord.gg/vMGH5QywTH) channels! And with that, this tutorial series is over, congratulations! Through this tutorial, we've built an auction contract and iterated on it adding improvements and extending its functionality, created a frontend to interact with the auction, used an API to index previous bids, and deployed a factory contract to make deploying new auctions easier. Along the way we've learned a great deal about NEAR, we learned about the anatomy of smart contracts, how to lock a contract to make it more secure, how to use primitives such as NFTs and FTs, how to perform cross-contract calls, how to use wallets from a frontend to interact with the blockchain and display data about a smart contract, how to pull historical data from the blockchain using an API, how to deploy contracts from other contracts and a lot of other little bits that will help you in the future. That's a lot, so once again congratulations! --- # Source: https://docs.near.org/web3-apps/backend/backend-login.md --- id: backend-login title: Authenticate NEAR Users description: "Learn how to authenticate NEAR users in your backend service by creating challenges, requesting wallet signatures, and verifying signatures." --- Recently NEAR has approved a new standard that, among other things, enables users to authenticate into a backend service. The basic idea is that the user will sign a challenge with their NEAR wallet, and the backend will verify the signature. If the signature is valid, then the user is authenticated. --- ## Backend Auth with a NEAR Wallet Authenticating users is a common use-case for backends and web applications. This enables services to provide a personalized experience to users, and to protect sensitive data. To authenticate a user, the backend must verify that the user is who they say they are. To do so, the backend must verify that the user has access to a full-access key that is associated with their account. For this three basic steps are needed: 1. Create a challenge for the user to sign. 2. Ask the user to sign the challenge with the wallet. 3. Verify the signature corresponds to the user. ### 1. Create a Challenge Assume we want to login the user into our application named `application-name`. We first need to create a challenge that the user will sign with their wallet. For this, it is recommended to use a cryptographically secure random number generator to create the challenge. ```js const challenge = randomBytes(32) const message = 'Login with NEAR' ``` :::note Here we use [crypto.randomBytes](https://nodejs.org/api/crypto.html#crypto_crypto_randombytes_size_callback) to generate a 32 byte random buffer. ::: ### 2. Ask the User to Sign the Challenge The `signMessage` method needed to sign the challenge is supported by these wallets: - Meteor Wallet - Here Wallet - Near Snap - Nightly Wallet - WELLDONE Wallet - NearMobileWallet - MyNearWallet - Sender - Intear Wallet The message that the user needs to sign contains 4 fields: - Message: The message that the user is signing. - Recipient: The recipient of the message. - Nonce: The challenge that the user is signing. - Callback URL: The URL that the wallet will call with the signature. ```js // Assuming you setup a wallet selector so far const signature = wallet.signMessage({ message, recipient, nonce: challenge, callbackUrl: }) ``` ### 3. Verify the Signature Once the user has signed the challenge, the wallet will call the `callbackUrl` with the signature. The backend can then verify the signature. ``` import { verifyMessage } from "near-api-js/nep413"; import { JsonRpcProvider } from "near-api-js"; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // This is the challenge given to the user to sign const MESSAGE: string = "log me in"; const APP: string = "http://localhost:3000"; const CHALLENGE: Buffer = Buffer.from(Array.from(Array(32).keys())); // This is the object returned by `wallet.signMessage` in wallet selector const walletReturn = { "signature": "IfModLa3g3czlyPhkg/LSkTFSy7XCGreStZJTDIO1m3viEnYFLdXfpz1gYUVKYv3W2vwcV77TmGEzc9y0Nz+AA==", "accountId": "maguila.testnet", "publicKey": "ed25519:AtH7GEjv2qmBVoT8qoRhWXizXM5CC12DC6tiqY9iNoRm" }; await verifyMessage({ signerAccountId: walletReturn.accountId, signerPublicKey: walletReturn.publicKey, signature: new Uint8Array(Buffer.from(walletReturn.signature, 'base64')), payload: { message: MESSAGE, recipient: APP, nonce: CHALLENGE }, provider }); ``` --- # Source: https://docs.near.org/integrations/balance-changes.md --- id: balance-changes title: Balance changes sidebar_label: Balance Changes description: "Learn how to query and track account balances in NEAR protocol, including native NEAR tokens, fungible tokens, and balance management for integrations." --- This document provides an overview of how to track balance changes on NEAR accounts, including sending tokens and viewing balance changes using the NEAR CLI and RPC endpoints. - [NEAR Account](https://testnet.mynearwallet.com/create) - [NEAR-CLI](/tools/near-cli) - Credentials for sender account stored locally by running [`near login`](/tools/near-cli#import) ### Native NEAR (Ⓝ) {#native-near} > Balance changes on accounts can be tracked by using our [changes RPC endpoint](/api/rpc/contracts#view-account-changes). You can test this out by sending tokens to an account using [NEAR-CLI](/tools/near-cli#send-near) and then viewing the changes made. ## Send Tokens {#send-tokens} - Send tokens using [`near send`](../tools/cli.md#send-near) ```bash near tokens sender.testnet send-near receiver.testnet '1 NEAR' network-config testnet sign-with-keychain send ``` - You should see a result in your terminal that looks something like this: ```bash Sending 1 NEAR to receiver.testnet from sender.testnet Transaction Id 4To336bYcoGc3LMucJPMk6fMk5suKfCrdNotrRtTxqDy To see the transaction in the transaction explorer, please open this url in your browser https://testnet.nearblocks.io/txns/4To336bYcoGc3LMucJPMk6fMk5suKfCrdNotrRtTxqDy ``` ## View Balance Changes {#view-balance-changes} - Open the transaction URL in [NearBlocks Explorer](https://testnet.nearblocks.io/) and copy the `BLOCK HASH`. - Using the `BLOCK HASH` and the accountId, query the [changes RPC endpoint](/api/rpc/contracts#view-account-changes) to view changes. **Example Query using HTTPie:** ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare \ method=changes \ 'params:={ "block_id": "CJ24svU3C9FaULVjcNVnWuVZjK6mNaQ8p6AMyUDMqB37", "changes_type": "account_changes", "account_ids": ["sender.testnet"] }' ```
**Example Response:** ```json { "id": "dontcare", "jsonrpc": "2.0", "result": { "block_hash": "BRgE4bjmUo33jmiVBcZaWGkSLVeL7TTi4ZxYTvJdPbB9", "changes": [ { "cause": { "tx_hash": "4To336bYcoGc3LMucJPMk6fMk5suKfCrdNotrRtTxqDy", "type": "transaction_processing" }, "change": { "account_id": "sender.testnet", "amount": "11767430014412510000000000", "code_hash": "11111111111111111111111111111111", "locked": "0", "storage_paid_at": 0, "storage_usage": 806 }, "type": "account_update" } ] } } ```
--- Alternatively, you can view account balances by [querying `view_account`](/api/rpc/contracts#view-account) which only requires an accountId. **Example HTTPie Request:** ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=query \ params:='{ "request_type": "view_account", "finality": "final", "account_id": "sender.testnet" }' ``` **Example Response:** ```json { "id": "dontcare", "jsonrpc": "2.0", "result": { "amount": "11767430683960197500000000", "block_hash": "HUiscpNyoyR5z1UdnZhAJLNz1G8UjBrFTecSYqCrvdfW", "block_height": 50754977, "code_hash": "11111111111111111111111111111111", "locked": "0", "storage_paid_at": 0, "storage_usage": 806 } } ``` **Note:** Gas prices can change between blocks. Even for transactions with deterministic gas cost the cost in NEAR could also be different. You can query the gas price for recent blocks using the [`gas_price` RPC endpoint](/api/rpc/gas#gas-price). --- :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/tutorials/auction/basic-auction.md --- id: basic-auction title: Basic Auction description: "Learn how to build n auction smart contract on NEAR." --- In this section, we will analyze a simple auction contract, which allows users to place bids, track the highest bidder and claim tokens at the end of the auction. After, we will cover how to test the contract, as well as how to deploy it on `testnet`. :::info Documentation During this tutorial, we will be relying on the [Smart Contract Documentation](../../smart-contracts/quickstart.md) and its different sections ::: :::tip Prerequisites Make sure to read the [Prerequisites](./0-intro.md) section and install the necessary tools before starting this tutorial ::: --- ## Cloning the contract To get started we'll clone the [tutorial's repository](https://github.com/near-examples/auctions-tutorial) from GitHub. The repository contains the same smart contracts written in JavaScript (`./contract-ts`) and Rust (`./contract-rs`). Navigate to the folder of the language you prefer, and then to the `01-basic-auction` folder. ```bash git clone git@github.com:near-examples/auctions-tutorial.git cd contract-ts/01-basic-auction ``` ```bash git clone git@github.com:near-examples/auctions-tutorial.git cd contract-rs/01-basic-auction ``` :::info Frontend The repository also contains a frontend application that interacts with the contract. You can find it in the `frontends` folder. We will cover the frontend in a future section ::: --- ## The Contract's State The contract allows users to place bids using $NEAR tokens and keeps track of the highest bidder. Lets start by looking at how we define the contract's state, this is, the data that the contract will store. ``` class Bid { bidder: AccountId; bid: bigint; } @NearBindgen({ requireInit: true }) class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(0) }; auction_end_time: bigint = BigInt(0); auctioneer: AccountId = ""; claimed: boolean = false; ``` #### Decorator The first thing to notice is that the main class of the contract is marked using the `@NearBindgen` decorator, which allows also to further specify that the contract **must be initialized** before being used. #### Storage (aka State) Another important information revealed by the code is that a contract can store different types of data, in this case: - `highest_bid` is an instance of a `Bid` which stores: - `bid`: a `BigInt` representing an amount of $NEAR tokens in `yoctonear` (`1Ⓝ = 10^24 yⓃ`) - `bidder`: an `AccountId` that represents which account placed the bid - `auction_end_time` a `BigInt` representing a `unix timestamp` in **nanoseconds** - `auctioneer` an `AccountId` that states who can withdraw the funds at the end of the auction - `claimed` a `boolean` that tracks if the auctioneer has claimed the funds ``` #[near(serializers = [json, borsh])] #[derive(Clone)] pub struct Bid { pub bidder: AccountId, pub bid: NearToken, } #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { highest_bid: Bid, auction_end_time: U64, auctioneer: AccountId, claimed: bool, } ``` #### Macros A first thing to notice is the use of the `#[near(contract_state)]` macro to denote the main structure and derive the `PanicOnDefault` to specify that the contract **must be initialized** before being used. We also use the `#[near(serializers = [json, borsh])]` macro to enable both `borsh` and `JSON` (de)serialization of the `Bid` structure. As a rule of thumb: use the `json` serializer for structs that will be used as input / output of functions, and `borsh` for those that will be saved to state. #### Storage (aka State) Another important information revealed by the code is that the contract can store different types of data. - `highest_bid` is an instance of a `Bid` which stores: - `bid`: a `NearToken` which simplifies handling $NEAR token amounts - `bidder`: the `AccountId` that placed the bid - `auction_end_time` is a `U64` representing a `unix timestamp` in **nanoseconds** - `auctioneer` an `AccountId` that states who can withdraw the funds at the end of the auction - `claimed` a `boolean` that tracks if the auctioneer has claimed the funds :::tip Learn More You can read more about the contract's structure and the type of data it can store in the following documentation pages: - [Basic Contract's Anatomy](../../smart-contracts/anatomy/anatomy.md) - [Contract's State](../../smart-contracts/anatomy/storage.md) - [Data Types](../../smart-contracts/anatomy/types.md) ::: --- ## Initialization Function Lets now take a look at the initialization function, which we need to call to determine the time at which the auction will end. ``` @initialize({ privateFunction: true }) init({ end_time, auctioneer}: { end_time: bigint, auctioneer: AccountId}) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; this.auctioneer = auctioneer; } ``` #### Decorator We denote the initialization function using the `@initialize({ privateFunction: true })` decorator. The `privateFunction:true` denotes that the function can only be called by the account on which the contract is deployed. ``` #[init] #[private] // only callable by the contract's account pub fn init(end_time: U64, auctioneer: AccountId) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: NearToken::from_yoctonear(1), }, auction_end_time: end_time, claimed: false, auctioneer, } } ``` #### Macros We denote the initialization function using the `#[init]` macro. Notice that the initialization function needs to return an instance of `Self`, i.e. the contract's structure. Meanwhile, the `#[private]` denotes that the function can only be called by the account on which the contract is deployed. #### End Time The end time is represented using a `unix timestamp` in **nano seconds**, and needs to be given as a `String` when calling the initialization function. This is because smart contracts cannot receive numbers larger than `52 bits` and `unix timestamps` are represented in `64 bits`. #### Initial Bid Notice that we initialize the contract with a `1 yoctonear` bid, made from the `current account id`. This means that, after the contract is initialized, the first bid will be placed by the contract at 10^-24 NEAR. #### Claimed The `claimed` field is initialized as `false`, as the auctioneer has not claimed the funds yet. #### Auctioneer The auctioneer is set by the deployer on initialization and is the account that will be able to claim the funds at the end of the auction. :::tip Learn More You can read more about the contract's interface in our [contract functions documentation](../../smart-contracts/anatomy/functions.md), and learn about data types on the [data types documentation](../../smart-contracts/anatomy/types.md). ::: --- ## Read-only Functions The contract implements four functions to give access to its stored data, i.e. the highest bid so far (the amount and by whom), the time at which the auction ends, the auctioneer, and whether the auction has been claimed. ``` @view({}) get_highest_bid(): Bid { return this.highest_bid; } @view({}) get_auction_end_time(): BigInt { return this.auction_end_time; } @view({}) get_auctioneer(): AccountId { return this.auctioneer; } @view({}) get_claimed(): boolean { return this.claimed; } } ``` Functions that do not change the contract's state (i.e. that only read from it) are called `view` functions, and are decorated using the `@view` decorator. ``` pub fn get_highest_bid(&self) -> Bid { self.highest_bid.clone() } pub fn get_auction_end_time(&self) -> U64 { self.auction_end_time } pub fn get_auctioneer(&self) -> AccountId { self.auctioneer.clone() } pub fn get_claimed(&self) -> bool { self.claimed } } ``` Functions that do not change the contract's state (i.e. that only read from it) are called `view` functions and take a non-mutable reference to `self` (`&self`). View functions are **free to call**, and do **not require** a NEAR account to sign a transaction in order to call them. :::tip Learn More You can read more about the contract's interface in our [contract functions documentation](../../smart-contracts/anatomy/functions.md), and learn about data types on the [data types documentation](../../smart-contracts/anatomy/types.md). ::: --- ## Bidding Function An auction is not an auction if you can't place a bid! For this, the contract includes a `bid` function, which users will call attaching some $NEAR tokens. The function is quite simple: it verifies if the auction is still active and compares the attached deposit with the current highest bid. If the bid is higher, it updates the `highest_bid` and refunds the previous bidder. ``` @call({ payableFunction: true }) bid(): NearPromise { // Assert the auction is still ongoing assert(this.auction_end_time > near.blockTimestamp(), "Auction has ended"); // Current bid const bid = near.attachedDeposit(); const bidder = near.predecessorAccountId(); // Last bid const { bidder: lastBidder, bid: lastBid } = this.highest_bid; // Check if the deposit is higher than the current bid assert(bid > lastBid, "You must place a higher bid"); // Update the highest bid this.highest_bid = { bidder, bid }; // Save the new bid // Transfer tokens back to the last bidder return NearPromise.new(lastBidder).transfer(lastBid); } ``` ``` #[payable] pub fn bid(&mut self) -> Promise { // Assert the auction is still ongoing require!( env::block_timestamp() < self.auction_end_time.into(), "Auction has ended" ); // Current bid let bid = env::attached_deposit(); let bidder = env::predecessor_account_id(); // Last bid let Bid { bidder: last_bidder, bid: last_bid, } = self.highest_bid.clone(); // Check if the deposit is higher than the current bid require!(bid > last_bid, "You must place a higher bid"); // Update the highest bid self.highest_bid = Bid { bidder, bid }; // Transfer tokens back to the last bidder Promise::new(last_bidder).transfer(last_bid) } ``` #### Payable Functions The first thing to notice is that the function changes the state, and thus is marked with a `@call` decorator in JS, while taking as input a mutable reference to self (`&mut self`) on Rust. To call this function, a NEAR account needs to sign a transaction and expend GAS. Second, the function is marked as `payable`, this is because by default **functions do not accept $NEAR tokens**! If a user attaches tokens while calling a function that is not marked as `payable`, the transaction will fail. #### The Environment Notice that the function can access information about the environment in which it is running, such as who called the function (`predecessor account`), how many tokens they attached as deposit (`attached deposit`), and the approximate `unix timestamp` at which the function is executing (`block timestamp`). #### Token Transfer The function finishes by creating a `Promise` to transfer tokens to the previous bidder. This token amount will be deducted immediately and transferred in the next block after the current function has finished executing. Note that on the first bid, the contract will send 1 yoctonear to itself, this is fine as we can safely assume that the contract will have the lowest denomination of $NEAR available to send to itself.
Handling Funds When a user attaches tokens to a call, the tokens are deposited to the contract's account before the function is executed. However, if the function raises an error during its execution, the tokens are immediately refunded to the user.
:::tip Learn More You can read more about the environment variables, payable functions and which actions the contract can perform here: - [Environment Variables](../../smart-contracts/anatomy/environment.md) - [Payable Functions](../../smart-contracts/anatomy/functions.md) - [Transfers and Actions](../../smart-contracts/anatomy/actions.md) ::: --- ## Claim function You'll notice that the contract has a final function called `claim`, this allows the auctioneer to claim the funds from the contract at the end of the auction. Since, on NEAR, a smart contract account and user account are the same, contracts can still have keys when they are deployed, thus a user could just claim the funds from the contract via a wallet. However, this presents a security issue since by having a key the key holder can take the funds from the contract at any point, maliciously change the contract's state or just delete the contract as a whole. By implementing a `claim` function we can later lock the contract by removing all access keys and have the auctioneer claim the funds via preset conditions via code. ``` @call({}) claim() { assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); assert(!this.claimed, "Auction has been claimed"); this.claimed = true; return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) } ``` ``` pub fn claim(&mut self) -> Promise { require!( env::block_timestamp() > self.auction_end_time.into(), "Auction has not ended yet" ); require!(!self.claimed, "Auction has already been claimed"); self.claimed = true; // Transfer tokens to the auctioneer Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid) } ``` This function is quite simple it does four things: 1) Checks that the auction has ended (the current timestamp is past the auction end time). 2) Checks that the auction has not yet been claimed. 3) Sets the auction as now claimed. 4) And if these conditions hold true it transfers $NEAR equal to the highest bid to the auctioneer. :::tip Learn More You can read more about locking contracts in this section of the documentation: [locked accounts](../../protocol/access-keys.md#locked-accounts) ::: --- ## Conclusion In this part of the tutorial, we've seen how a smart contract stores data, mutates the stored data, and views the data. In the [next part](./1.2-testing.md), we will cover how to test the contract, so we can ensure it works as expected before deploying it to `testnet`. --- # Source: https://docs.near.org/smart-contracts/tutorials/basic-contracts.md --- 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." --- 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/wallet-login.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 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 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-rs cargo test ``` ```bash cd contract-ts yarn yarn 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-rs cargo near deploy build-non-reproducible-wasm ``` ```bash cd contract-ts npm run build near deploy ./build/.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) --- # Source: https://docs.near.org/protocol/basics.md --- id: basics title: What is NEAR? description: "A scalable and secure chain with an amazing developer experience" --- NEAR is a **user-friendly** and [**carbon-neutral**](https://near.org/blog/near-climate-neutral-product/) blockchain, built to be [fast, secure, and infinitely scalable](https://www.leewayhertz.com/comparison-of-blockchain-protocols#Parallel-comparison-of-various-blockchain-networks). It offers a simple user experience with named accounts, low fees, and a robust developer ecosystem. ![img](/assets/docs/welcome-pages/1.near-protocol.png) In technical terms, NEAR is a [layer one](https://coinmarketcap.com/academy/glossary/layer-1-blockchain), [sharded](https://near.org/blog/near-launches-nightshade-sharding-paving-the-way-for-mass-adoption), [proof-of-stake](https://en.wikipedia.org/wiki/Proof_of_stake) blockchain built with usability in mind. In simpler terms, NEAR is the **blockchain for everyone**.
What do these Technical Terms mean? In technical terms, NEAR is a [layer-one](https://coinmarketcap.com/academy/glossary/layer-1-blockchain), [sharded](https://near.org/blog/near-launches-nightshade-sharding-paving-the-way-for-mass-adoption), [proof-of-stake](https://en.wikipedia.org/wiki/Proof_of_stake) blockchain built with usability in mind. [Layer-1](https://coinmarketcap.com/academy/glossary/layer-1-blockchain) means NEAR is the foundation that supports everything else built on it. It keeps all the transaction records safe and unchangeable which keeps the network secure and trustworthy. [Sharded](https://near.org/blog/near-launches-nightshade-sharding-paving-the-way-for-mass-adoption) means the network is broken into pieces that work in parallel. This helps NEAR process transactions quickly and efficiently. [Proof-of-stake](https://en.wikipedia.org/wiki/Proof_of_stake) uses less electricity compared with other blockchains which use proof-of-work. Users show they own NEAR tokens to help run the network. This makes it cheaper and lets more people use it.
--- ## Why Choose NEAR? {#why-build-on-near} NEAR is a technical marvel, offering built-in features such as named accounts and account abstraction. For developers, NEAR offers everything needed for their applications, from smart contracts to indexers. All while being interoperable with other chains. ### ⭐ Simple to Use 1. Use [**named accounts**](./account-model.md) like `alice.near` 2. Simple sign-up: create an [account for free](../tutorials/protocol/create-account.md), login [with socials](../web3-apps/tutorials/wallet-login.md) or [telegram](https://web.telegram.org/k/#@herewalletbot) 3. Transactions are **fast** _(~1.3s finality)_ and **cheap** _(< 1¢ in fees)_ 4. You don't need to buy crypto thanks to **built-in account abstraction** 5. [Access Keys](./access-keys.md) make it safe and easy to use 6. Control accounts on **other chains** thanks to [chain signatures](../chain-abstraction/chain-signatures.md) ### 🛡️ Battle-Tested 1. 5 years of **100% uptime** and [**4 Billion** transactions](https://pikespeak.ai/near-world/overview) processed 2. NEAR has sustained peaks of [>13M transactions](https://pikespeak.ai/near-world/overview) in a single day 3. NEAR is home to decentralized apps with [millions of users](https://dappradar.com/rankings/protocol/near?sort=uawCount&order=desc&range=30d): - [Kai-ching](https://cosmose.ai/) - [Sweat](https://sweateconomy.com/) - [Hot Wallet](https://t.me/herewalletbot/) ### 🧑‍💻 Great Developer Experience 1. Build smart contracts with **Javascript** or **Rust** 2. **Simple onboarding**, thanks to its complete documentation and examples 3. Get answers and learn at NEAR DevRel **office hours**, where anybody can participate 4. Earn from your contract's gas fees 5. **EVM compatible** with [Project Aurora](http://www.aurora.dev) _(Deploy your Solidity contracts with ease)_ ### ♻️ Environmentally Friendly 1. NEAR is **[certified carbon-neutral](https://near.org/blog/the-near-blockchain-is-climate-neutral/)** 2. NEAR **consumes in a year** the same energy [**bitcoin consumes in 3 minutes**](https://medium.com/nearprotocol/how-near-went-carbon-neutral-e656db96da47#:~:text=The%20firm%20found%20that%20NEAR,PoS%20technology%20instead%20of%20PoW) --- # Source: https://docs.near.org/smart-contracts/anatomy/best-practices.md --- id: best-practices title: Best Practices description: "A collection of best practices for writing smart contracts on NEAR." --- This page provides a collection of best practices for writing smart contracts on NEAR. These practices are designed to help you write secure, efficient, and maintainable code. # Best practices Here we lay out some best practices for writing smart contracts on NEAR, such as: - [Store Account IDs efficiently](#store-account-ids-efficiently) - [Enable overflow checks](#enable-overflow-checks) - [Use `require!` early](#use-require-early) - [Use `log!`](#use-log) - [Return `Promise`](#return-promise) - [Reuse crates from `near-sdk`](#reuse-crates-from-near-sdk) - [The difference between `std::panic!` and `env::panic`](#stdpanic-vs-envpanic) - [Use workspaces](#use-workspaces) --- ## Store Account IDs efficiently You can save on smart contract storage if using NEAR Account IDs by encoding them using base32. Since they consist of `[a-z.-_]` characters with a maximum length of 64 characters, they can be encoded using 5 bits per character, with terminal `\0`. Going to a size of 65 * 5 = 325 bits from the original (64 + 4) * 8 = 544 bits. This is a 40% reduction in storage costs ## Enable overflow checks It's usually helpful to panic on integer overflow. To enable it, add the following into your `Cargo.toml` file: ```toml [profile.release] overflow-checks = true ``` ## Use `require!` early Try to validate the input, context, state and access using `require!` before taking any actions. The earlier you panic, the more [gas](/protocol/gas) you will save for the caller. ```rust #[near] impl Contract { pub fn set_fee(&mut self, new_fee: Fee) { require!(env::predecessor_account_id() == self.owner_id, "Owner's method"); new_fee.assert_valid(); self.internal_set_fee(new_fee); } } ``` **Note**: If you want debug information in the panic message or if you are using an SDK version before `4.0.0-pre.2`, the Rust `assert!` macro can be used instead of `require!`. ```rust #[near] impl Contract { pub fn set_fee(&mut self, new_fee: Fee) { assert_eq!(env::predecessor_account_id(), self.owner_id, "Owner's method"); new_fee.assert_valid(); self.internal_set_fee(new_fee); } } ``` ## Use `log!` Use logging for debugging and notifying user. When you need a formatted message, you can use the following macro: ```rust log!("Transferred {} tokens from {} to {}", amount, sender_id, receiver_id); ``` It's equivalent to the following message: ```rust env::log_str(format!("Transferred {} tokens from {} to {}", amount, sender_id, receiver_id).as_ref()); ``` ## Return `Promise` If your method makes a cross-contract call, you probably want to return the newly created `Promise`. This allows the caller (such as a near-cli or near-api-js call) to wait for the result of the promise instead of returning immediately. Additionally, if the promise fails for some reason, returning it will let the caller know about the failure, as well as enabling NEAR Explorer and other tools to mark the whole transaction chain as failing. This can prevent false-positives when the first or first few transactions in a chain succeed but a subsequent transaction fails. E.g. ```rust #[near] impl Contract { pub fn withdraw_100(&mut self, receiver_id: AccountId) -> Promise { Promise::new(receiver_id).transfer(100) } } ``` ## Reuse crates from `near-sdk` `near-sdk` re-exports the following crates: - `borsh` - `base64` - `bs58` - `serde` - `serde_json` Most common crates include `borsh` which is needed for internal STATE serialization and `serde` for external JSON serialization. When marking structs with `serde::Serialize` you need to use `#[serde(crate = "near_sdk::serde")]` to point serde to the correct base crate. ```rust /// Main contract structure serialized with Borsh #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { pub pair: Pair, } /// Implements both `serde` and `borsh` serialization. /// `serde` is typically useful when returning a struct in JSON format for a frontend. #[near(serializers = [json, borsh])] pub struct Pair { pub a: u32, pub b: u32, } #[near] impl Contract { #[init] pub fn new(pair: Pair) -> Self { Self { pair, } } pub fn get_pair(self) -> Pair { self.pair } } ``` ## `std::panic!` vs `env::panic` - `std::panic!` panics the current thread. It uses `format!` internally, so it can take arguments. SDK sets up a panic hook, which converts the generated `PanicInfo` from `panic!` into a string and uses `env::panic` internally to report it to Runtime. This may provide extra debugging information such as the line number of the source code where the panic happened. - `env::panic` directly calls the host method to panic the contract. It doesn't provide any other extra debugging information except for the passed message. ## Use workspaces Workspaces allow you to automate workflows and run tests for multiple contracts and cross-contract calls in a sandbox or testnet environment. Read more, [workspaces-rs](https://github.com/near/workspaces-rs) or [workspaces-js](https://github.com/near/workspaces-js). Here we lay out some best practices for writing smart contracts in Python on NEAR: - [Validate early](#validate-early) - [Use proper logging](#use-proper-logging) - [Return Promises](#return-promises) - [Handle storage efficiently](#handle-storage-efficiently) - [Use type hints](#use-type-hints) - [Follow security patterns](#follow-security-patterns) --- ## Validate early Validate inputs, context, and state as early as possible in your functions. This saves gas by failing fast before executing expensive operations. ```python from near_sdk_py import call, Context class Contract: def __init__(self): self.owner_id = Context.current_account_id() @call def set_config(self, config_data): # Validate permissions early if Context.predecessor_account_id() != self.owner_id: raise Exception("Only owner can modify config") # Validate inputs early if "parameter" not in config_data or not isinstance(config_data["parameter"], int): raise Exception("Invalid config: missing or invalid parameter") # Only proceed after validation self._update_config(config_data) ``` ## Use proper logging Use the `Log` utility for structured logging. This allows external services to parse events from your contract easily. ```python from near_sdk_py import call, Log @call def transfer(self, receiver_id, amount): # Business logic here... # Use informational logs for regular updates Log.info(f"Transferred {amount} tokens to {receiver_id}") # Use structured event logging for indexable events Log.event("transfer", { "sender": Context.predecessor_account_id(), "receiver": receiver_id, "amount": amount }) ``` ## Return Promises When making cross-contract calls, return the Promise object to let the caller track the result. This is especially important for transactions that need to be monitored. ```python from near_sdk_py import call from near_sdk_py.promises import Contract @call def withdraw(self, amount, receiver_id): # Perform validations and business logic... # Return the promise for better caller experience return Contract(receiver_id).call( "deposit", amount=amount, sender=Context.predecessor_account_id() ) ``` ## Handle storage efficiently Use the SDK collections for efficient storage handling, especially for growing data sets. ```python from near_sdk_py.collections import UnorderedMap, Vector class TokenContract: def __init__(self): # Use SDK collections for efficient storage self.tokens = Vector("t") # Efficiently stores ordered items self.balances = UnorderedMap("b") # Efficiently stores key-value pairs @call def mint(self, token_id): # Add to vector without loading all tokens self.tokens.append(token_id) # Update balance without loading all balances current = self.balances.get(Context.predecessor_account_id(), 0) self.balances[Context.predecessor_account_id()] = current + 1 ``` ## Use type hints Python's type hints improve code readability and can help catch errors during development. ```python from typing import Dict, List, Optional from near_sdk_py import view, call class Contract: def __init__(self): self.data: Dict[str, int] = {} @view def get_value(self, key: str) -> Optional[int]: return self.data.get(key) @call def set_values(self, updates: Dict[str, int]) -> List[str]: updated_keys = [] for key, value in updates.items(): self.data[key] = value updated_keys.append(key) return updated_keys ``` ## Follow security patterns Apply security best practices to protect your contract from common vulnerabilities. ```python from near_sdk_py import call, Context from near_sdk_py.constants import ONE_NEAR class Contract: def __init__(self): self.owner = Context.current_account_id() self.minimum_deposit = ONE_NEAR // 100 # 0.01 NEAR @call def deposit(self): # Check sufficient deposit to prevent spam deposit = Context.attached_deposit() if deposit < self.minimum_deposit: raise Exception(f"Minimum deposit is {self.minimum_deposit}") # Re-entrancy protection pattern current_balance = self.balances.get(Context.predecessor_account_id(), 0) # Update state before external calls self.balances[Context.predecessor_account_id()] = current_balance + deposit # Only after state update, perform any external calls # ... ``` --- # Source: https://docs.near.org/tutorials/auction/bidding-with-fts.md --- id: bidding-with-fts title: Bidding with FTs description: "Learn how to enable bidding with fungible tokens" --- To further develop this contract we will introduce another primitive: [fungible tokens](../../primitives/ft/ft.md). Instead of placing bids in $NEAR tokens, they will be placed in FTs. This may be useful if, for example, an auctioneer wants to keep the bid amounts constant in terms of dollars as an auction is carried out, so bids can be placed in stablecoins such as $USDC. Another use case is if a project like Ref Finance was holding its own auction and wanted the auction to happen in its project's token $REF. --- ## Specifying the FT contract We want to only accept bids in one type of fungible token; accepting many different FTs would make the value of each bid difficult to compare. We're also going to adjust the contract so that the auctioneer can specify a starting bid amount for the auction. ``` init({ end_time, auctioneer, ft_contract, nft_contract, token_id, starting_price }: { end_time: bigint, auctioneer: AccountId, ft_contract: AccountId, nft_contract: AccountId, token_id: string, starting_price: bigint }) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: starting_price }; this.auctioneer = auctioneer; this.ft_contract = ft_contract; this.nft_contract = nft_contract; this.token_id = token_id; } ``` ``` pub fn init( end_time: U64, auctioneer: AccountId, ft_contract: AccountId, nft_contract: AccountId, token_id: TokenId, starting_price: U128, ) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: starting_price, }, auction_end_time: end_time, auctioneer, claimed: false, ft_contract, nft_contract, token_id, } } ``` --- ## Accepting bids in FTs When we were making bids in $NEAR tokens we would call the auction contract directly and attach $NEAR tokens to the call. With fungible tokens, since an account's balance lives on a separate contract, we call the FT contract which then calls the auction contract and transfers tokens. The method on the FT contract to do this is named `ft_transfer_call` and it will always call a method in the target contract named `ft_on_transfer`. Take a look [here](../../primitives/ft/ft.md#attaching-fts-to-a-call) for more information. ![ft_transfer_call-flow](/assets/docs/tutorials/auction/auction-ft-transfer.png) The `ft_on_transfer` method always has the same interface; the FT contract will pass it the `sender`, the `amount` of FTs being sent and a `msg` which can be empty (which it will be here) or it can contain some information needed by the method (if you want to send multiple arguments in msg it is best practice to deliver this in JSON then parse it in the contract). The method returns the number of tokens to refund the user, in our case we will use all the tokens attached to the call for the bid unless the contract panics in which case the user will automatically be refunded their FTs in full. ``` ft_on_transfer({ sender_id, amount, msg }: { sender_id: AccountId, amount: bigint, msg: String }) { ``` ``` #[allow(unused_variables, unused_must_use)] pub fn ft_on_transfer(&mut self, sender_id: AccountId, amount: U128, msg: String) -> U128 { ``` We need to confirm that the user is attaching fungible tokens when calling the method and that they are using the right FT, this is done by checking the predecessor's account ID. Since it's the FT contract that directly calls the auction contract, the `predecessor` is now the account ID of the FT contract. ``` assert(near.predecessorAccountId() == this.ft_contract, "The token is not supported"); assert(BigInt(amount) >= BigInt(previous.bid), "You must place a higher bid"); ``` ``` let ft = env::predecessor_account_id(); require!(ft == self.ft_contract, "The token is not supported"); ``` The bidder's account ID is now given by the argument `sender_id` and the bid amount is passed as an argument named `amount`. ``` this.highest_bid = { bidder: sender_id, bid: amount, }; ``` ``` // Update the highest bid self.highest_bid = Bid { bidder: sender_id, bid: amount, }; ``` When we want to return the funds to the previous bidder we now make a cross-contract call to the FT contract. ``` return NearPromise.new(this.ft_contract) .functionCall("ft_transfer", JSON.stringify({ receiver_id: previous.bidder, amount: previous.bid }), BigInt(1), THIRTY_TGAS) .then( NearPromise.new(near.currentAccountId()) .functionCall("ft_transfer_callback", JSON.stringify({}), NO_DEPOSIT, THIRTY_TGAS) ) .asReturn() } ``` ``` @call({ privateFunction: true }) ft_transfer_callback({ }): BigInt { return BigInt(0); } ``` In JavaScript, we have to return the Promise to transfer the FTs but we also need to return how much to refund the user. So after transferring the FTs, we make a `callback` to our own contract to resume the contract flow. Note that the callback is private so it can only be called by the contract. We return 0 because the method uses all the FTs in the call. ``` // Transfer FTs back to the last bidder ft_contract::ext(self.ft_contract.clone()) .with_attached_deposit(NearToken::from_yoctonear(1)) .with_static_gas(Gas::from_tgas(30)) .ft_transfer(last_bidder, last_bid); ``` ``` #[ext_contract(ft_contract)] #[allow(dead_code)] trait FT { fn ft_transfer(&self, receiver_id: AccountId, amount: U128); } ``` We then return 0 because the method uses all the FTs in the call. ``` U128(0) ``` If the call was to fail the FT contract will automatically refund the user their FTs.
What happens if the cross-contract call fails? The first time this method is called the contract will try to send itself FTs. Most fungible token contracts don't allow one to send themselves FTs so the cross-contract call will fail. However, since cross-contract calls are asynchronous and independent and we are not checking the result of the call then the auction contract does not care that the call failed and ft_on_transfer will complete successfully. In the other cases, the call to the fungible token contract could only fail if the receiver does not exist, the FT contract does not exist, the auction contract doesn't have enough fungible tokens to cover the amount being sent, or the receiver is not registered in the FT contract. Our contract is set up such that these errors cannot occur, the receiver must exist since they placed the previous bid, the FT contract exists since it was used to place the bid, the auction contract has enough FTs to cover the amount since it was sent that amount by the previous bid, and the receiver must be registered in the FT contract since they needed to have held the token in the first place to make a bid.
--- ## Claiming the FTs When the auction is complete we need to send the fungible tokens to the auctioneer when we send the NFT to the highest bidder, we implement a similar call as when we were returning the funds just changing the arguments. ``` return NearPromise.new(this.nft_contract) .functionCall("nft_transfer", JSON.stringify({ receiver_id: this.highest_bid.bidder, token_id: this.token_id }), BigInt(1), THIRTY_TGAS) .then(NearPromise.new(this.ft_contract) .functionCall("ft_transfer", JSON.stringify({ receiver_id: this.auctioneer, amount: this.highest_bid.bid }), BigInt(1), THIRTY_TGAS)) .asReturn() } ``` In JavaScript, since we need to return each cross-contract call we chain the NFT and FT transfer. ``` self.claimed = true; // Transfer FTs to the auctioneer ft_contract::ext(self.ft_contract.clone()) .with_attached_deposit(NearToken::from_yoctonear(1)) .with_static_gas(Gas::from_tgas(30)) .ft_transfer(self.auctioneer.clone(), self.highest_bid.bid); // Transfer the NFT to the highest bidder nft_contract::ext(self.nft_contract.clone()) .with_static_gas(Gas::from_tgas(30)) .with_attached_deposit(NearToken::from_yoctonear(1)) ``` --- ## Creating a new FT Just as with the NFT contract, we will deploy an FT contract in the sandbox tests using a WASM file compiled from [this repo](https://github.com/near-examples/FT). When the contract is deployed it is initialized with `new_default_meta` which sets the token's metadata, including things like its name and symbol, to default values while requiring the owner (where the token supply will sent), and the total supply of the token. ``` const ft_contract = await root.devDeploy(FT_WASM_FILEPATH); await ft_contract.call(ft_contract,"new_default_meta",{"owner_id":ft_contract.accountId,"total_supply":BigInt(1_000_000).toString()}); ``` ``` let bob = create_subaccount(&sandbox, "bob.sandbox").await?; let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; let nft_contract = create_subaccount(&sandbox, "nft-contract.sandbox") .await? .as_contract(); let ft_contract = create_subaccount(&sandbox, "ft-contract.sandbox") .await? .as_contract(); let contract = create_subaccount(&sandbox, "contract.sandbox") .await? .as_contract(); // Initialize signer for the contract deployment let signer = near_api::Signer::from_secret_key( near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY ``` --- ## Registering users in the FT contract For one to receive fungible tokens, first their account ID must be [registered](../../primitives/ft/ft.md#registering-a-user) in the FT contract. A user has to register in an FT contract to pay for the storage used to track their amount of tokens. By default, a contract pays for its own storage, but not requiring a user to register and pay for storage would drain the contract of $NEAR tokens. When the contract is live we don't need to register the accounts that we transfer tokens back to since to make a bid in the first place they would have needed to be registered, but we do need to register the auction contract in the FT contract to receive bids and the auctioneer to receive the funds at the end of the auction. It is most convenient to register users from the frontend rather than the contract. In our tests, since we are creating a new fungible token and new accounts we will actually have to register every account that will interact with FTs. ``` const contracts = [alice,bob,contract,auctioneer]; for (const contract_to_register of contracts) { await contract_to_register.call(ft_contract, "storage_deposit",{ "account_id": contract_to_register.accountId },{ attachedDeposit: NEAR.from("8000000000000000000000").toString(),gas: "300000000000000" }) } ``` ``` "token_metadata": { "title": "LEEROYYYMMMJENKINSSS", "description": "Alright time's up, let's do this.", "media": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse3.mm.bing.net%2Fth%3Fid%3DOIP.Fhp4lHufCdTzTeGCAblOdgHaF7%26pid%3DApi&f=1" }, }); nft_contract .call_function("nft_mint", request_payload) .transaction() .deposit(NearToken::from_millinear(80)) .with_signer(nft_contract.account_id().clone(), signer.clone()) .send_to(&sandbox_network) .await? .assert_success(); // Register accounts for account in [ ``` --- ## Simple FT transfer to bidders Then we will transfer the bidders FTs so they can use them to bid. A simple transfer of FTs is done using the method `ft_transfer` on the FT contract. ``` await ft_contract.call(ft_contract,"ft_transfer",{"receiver_id":alice.accountId,"amount":BigInt(150_000).toString()},{ attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" }); await ft_contract.call(ft_contract,"ft_transfer",{"receiver_id":bob.accountId,"amount":BigInt(150_000).toString()},{ attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" }); ``` ``` bob.clone(), contract.as_account().clone(), auctioneer.clone(), ] .iter() { ft_contract .call_function( "storage_deposit", ``` ``` .call_function("claim", ()) .transaction() .gas(NearGas::from_tgas(300)) .with_signer(auctioneer.account_id().clone(), signer.clone()) .send_to(&sandbox_network) .await? .assert_failure(); // Fast forward 200 blocks let blocks_to_advance = 200; sandbox.fast_forward(blocks_to_advance).await?; // Auctioneer claims auction contract .call_function("claim", ()) .transaction() .gas(NearGas::from_tgas(300)) .with_signer(auctioneer.account_id().clone(), signer.clone()) ``` --- ## FT transfer call As stated previously, to bid on the auction the bidder now calls `ft_transfer_call` on the FT contract which subsequently calls the auction contract's `ft_on_transfer` method with fungible tokens attached. ``` await alice.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(50_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" }); highest_bid = await contract.view("get_highest_bid", {}); t.is(highest_bid.bidder, alice.accountId); t.is(highest_bid.bid, BigInt(50_000).toString()); ``` ``` near_api::Contract::deploy(contract.account_id().clone()) .use_code(contract_wasm) .with_init_call( "init", serde_json::json!({ "end_time": a_minute_from_now.to_string(), "auctioneer": auctioneer.account_id(), "ft_contract": ft_contract.account_id(), "nft_contract": nft_contract.account_id(), "token_id": "1", "starting_price": starting_price }), )? .with_signer(signer.clone()) ``` ``` // Check highest bidder received the NFT let token_info: serde_json::Value = nft_contract .call_function("nft_token", serde_json::json!({"token_id": "1"})) .read_only() .fetch_from(&sandbox_network) .await? .data; let owner_id: String = token_info["owner_id"].as_str().unwrap().to_string(); assert_eq!( owner_id, bob.account_id().to_string(), "token owner is not the highest bidder" ); // Auctioneer claims auction back but fails ``` --- ## Checking users' FT balance Previously, to check a user's $NEAR balance, we pulled the details from their account. Now we are using FTs we query the balance on the FT contract using `ft_balance_of`, let's check that the contract's balance increased by the bid amount and the user's balance decreased by the bid amount. ``` contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId}) t.is(contract_balance, BigInt(50_000).toString()); alice_balance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId}) t.is(alice_balance, BigInt(100_000).toString()); ``` ``` .send_to(&sandbox_network) .await? .assert_success(); // Alice makes bid less than starting price ``` ``` .send_to(&sandbox_network) .await? .assert_success(); // Contract balance has been cleared let contract_balance: U128 = ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; assert_eq!(contract_balance, U128(0)); // Auctioneer balance has increased let auctioneer_balance: U128 = ft_balance_of(&ft_contract, auctioneer.account_id(), &sandbox_network).await?; assert_eq!(auctioneer_balance, U128(60_000)); ``` --- ## Invalid FT transfer call If we make a lower bid than the previous this will cause the auction contract to panic. One might expect that `ft_transfer_call` will fail, but it does not. `ft_on_transfer` will fail and the FT contract will recognize this and reverse the transfer of tokens. So after making an invalid bid, we should check that the call was successful but the parties involved in the transaction (the bidder and the contract) have the same balance of fungible tokens as they did before the call. Previous to this, Bob made a bid of 60,000 and Alice was returned her bid bringing her balance back up to 150,000. Now when Alice makes an invalid of 50,000 Alice's balance should remain at 150,000 and the contract should remain at a balance of 60,000. ``` await alice.call(ft_contract, "ft_transfer_call", { "receiver_id": contract.accountId,"amount": BigInt(50_000).toString(),"msg":""}, { attachedDeposit: NEAR.from("1").toString(),gas: "300000000000000" }); highest_bid = await contract.view("get_highest_bid", {}); t.is(highest_bid.bidder, bob.accountId); t.is(highest_bid.bid, BigInt(60_000).toString()); contract_balance = await ft_contract.view("ft_balance_of",{"account_id": contract.accountId}) t.is(contract_balance, BigInt(60_000).toString()); alice_balance = await ft_contract.view("ft_balance_of",{"account_id": alice.accountId}) t.is(alice_balance, BigInt(150_000).toString()); ``` ``` ft_balance_of(&ft_contract, contract.account_id(), &sandbox_network).await?; assert_eq!(contract_balance, U128(0)); // Alice balance has not changed yet let alice_balance: U128 = ft_balance_of(&ft_contract, alice.account_id(), &sandbox_network).await?; assert_eq!(alice_balance, U128(150_000)); // Alice makes valid bid ft_transfer_call( &ft_contract, &alice, contract.account_id(), U128(50_000), &signer, &sandbox_network, ) .await?; ``` --- ## Using FTs with the CLI If you want to interact with the auction contract you're going to need FTs. For this example, we'll use $DAI where the contract address is `dai.fakes.testnet`. One can easily acquire FTs through the [testnet faucet](https://near-faucet.io/). Select DAI and withdraw to the account you will use to place a bid. If you take a look at the transaction details you can see that the faucet registers your account in the FT contract and then sends you DAI from the faucet account. When deploying the contract make sure to specify the FT contract `dai.fakes.testnet`. The auction contract will need to be registered as well, you could do this by sending it an arbitrary amount of $DAI from the faucet or you can just register it since it doesn't need any FTs. You should also register the auctioneer, ```bash near call dai.fakes.testnet storage_deposit '{"account_id": ""}' --useAccount --deposit 0.1 ``` Now you can go ahead and place a bid. DAI has 18 decimals meaning that 1 $DAI is made up of 10^24 smallest units. To make a bid of 2 $DAI you can use the command: ```bash near call dai.fakes.testnet ft_transfer_call '{"receiver_id": "", "amount": "2000000000000000000", "msg": ""}' --useAccount --depositYocto 1 ``` ## Auction architecture When creating an application there are numerous ways to structure it. Here, we have one contract per auction meaning we have to deploy a new contract each time we want to host an auction. To make this easier we will leverage a factory contract to deploy auction contracts for an auctioneer. Deploying code for each auction gets expensive, with 100kb of storage costing 1 $NEAR, since each auction stores all the same type of information and implements the same methods one could instead decide to have multiple auctions per contract. In such case, the Contract struct would be a map of auctions. We would implement a method to create a new auction by adding an entry to the map with the specific details of that individual auction. ```javascript class Contract { auctions: UnorderedMap ``` ```rust pub struct Contract { auctions: IterableMap ``` However, this architecture could be deemed less secure since if a bad actor were to gain access to the contract they would have access to every auction instead of just one. --- ## Conclusion In this section, you learned a lot about fungible tokens: how to send and receive FTs in a smart contract, and then in sandbox tests how to deploy and initialize an FT contract, how to register a user in an FT contract, and send them some tokens, how to attach FTs to a smart contract call and finally how to view the FT balance of a user. With that, we now have our completed auction smart contract! Taking a further step back we've taken a very simple auction contract and transformed it into a more production contract with thorough testing. To improve the auction we learned how to add a prize by introducing NFTs, and enabled auctioneers to host auctions with FTs. In the [next part of the tutorial](./3.3-new-frontend.md), we're going to update the frontend to interact with the new features of the contract. --- # Source: https://docs.near.org/data-infrastructure/big-query.md --- id: big-query title: BigQuery Public Dataset sidebar_label: BigQuery description: "Learn how to use NEAR Protocol's BigQuery public dataset for blockchain data analysis, including querying on-chain data, understanding costs, and accessing historical transaction data." --- This document provides an overview of the BigQuery public dataset that allows users to query historical on-chain data from the NEAR Protocol. It includes setup instructions, example queries, and information about the available data structures. # NEAR Public Lakehouse Blockchain data indexing in NEAR Public Lakehouse is for anyone wanting to understand blockchain data. This includes: - **Users**: create queries to track NEAR assets, monitor transactions, or analyze on-chain events at a massive scale. - **Researchers**: use indexed data for data science tasks, including on-chain activities, identifying trends, or feeding AI/ML pipelines for predictive analysis. - **Startups**: can use NEAR's indexed data for deep insights on user engagement, smart contract utilization, or insights across tokens and NFT adoption. Benefits: - **NEAR instant insights**: Historical on-chain data queried at scale. - **Cost-effective**: eliminate the need to store and process bulk NEAR protocol data; query as little or as much data as preferred. - **Easy to use**: no prior experience with blockchain technology is required; bring a general knowledge of SQL to unlock insights. ## Getting started 1. Login into your [Google Cloud Account](https://console.cloud.google.com/). 2. Open the [NEAR Protocol BigQuery Public Dataset](https://console.cloud.google.com/bigquery?p=bigquery-public-data&d=crypto_near_mainnet_us&page=dataset). 3. Click in the VIEW DATASET button. 4. Click in the + to create a new tab and write your query, click in the RUN button, and check the `Query results` below the query. 5. Done :) :::info The [NEAR Public Lakehouse repository](https://github.com/near/near-public-lakehouse) contains the source code for ingesting NEAR Protocol data stored as JSON files in AWS S3 by [NEAR Lake Indexer](https://github.com/near/near-lake-indexer). ::: ### Example Queries - _How many unique signers and accounts have interacted with my smart contract per day?_ ```sql SELECT ra.block_date collected_for_day, COUNT(DISTINCT t.signer_account_id) as total_signers, COUNT(DISTINCT ra.receipt_predecessor_account_id) as total_accounts FROM `bigquery-public-data.crypto_near_mainnet_us.receipt_actions` ra JOIN `bigquery-public-data.crypto_near_mainnet_us.receipt_origin_transaction` ro ON ro.receipt_id = ra.receipt_id JOIN `bigquery-public-data.crypto_near_mainnet_us.transactions` t ON ro.originated_from_transaction_hash = t.transaction_hash WHERE ra.action_kind = 'FUNCTION_CALL' AND ra.receipt_receiver_account_id = 'social.near' -- change to your contract GROUP BY 1 ORDER BY 1 DESC; ``` ## How much it costs? - NEAR pays for the storage and doesn't charge you to use the public dataset. > To learn more about BigQuery public datasets [check this page](https://cloud.google.com/bigquery/public-data). - Google GCP charges for the queries that you perform on the data. For example, in today's price "Sep 1st, 2023" the On-demand (per TB) query pricing is $6.25 per TB where the first 1 TB per month is free. > Check [Google's pricing page](https://cloud.google.com/bigquery/pricing#analysis_pricing_models) for detailed pricing info, options, and best practices. :::tip You can check how much data it will query before running it in the BigQuery console UI. Again, since BigQuery uses a columnar data structure and partitions, it's recommended to select only the columns and partitions (`block_date`) needed to avoid unnecessary query costs. ::: ![Query Costs](/assets/docs/data-infrastructure/BQ_Query_Cost.png "BQ Query Costs") ## Architecture The data is loaded in a streaming fashion using [Databricks Autoloader](https://docs.gcp.databricks.com/ingestion/auto-loader/index.html) into raw/bronze tables, and transformed with [Databricks Delta Live Tables](https://www.databricks.com/product/delta-live-tables) streaming jobs into cleaned/enriched/silver tables. The silver tables are also copied into the [GCP BigQuery Public Dataset](https://cloud.google.com/bigquery/public-data). ![Architecture](/assets/docs/data-infrastructure/Architecture.png "Architecture") :::info [Databricks Medallion Architecture](https://www.databricks.com/glossary/medallion-architecture). ::: ## Available Data The current data that NEAR is providing was inspired by [NEAR Indexer for Explorer](https://github.com/near/near-indexer-for-explorer/). :::info NEAR plans to improve the data available in the NEAR Public Lakehouse making it easier to consume by denormalizing some tables. ::: The tables available in the NEAR Public Lakehouse are: - **blocks**: A structure that represents an entire block in the NEAR blockchain. `Block` is the main entity in NEAR Protocol blockchain. Blocks are produced in NEAR Protocol every second. - **chunks**: A structure that represents a chunk in the NEAR blockchain. `Chunk` of a `Block` is a part of a `Block` from a `Shard`. The collection of `Chunks` of the `Block` forms the NEAR Protocol Block. `Chunk` contains all the structures that make the `Block`: `Transactions`, [`Receipts`](https://nomicon.io/RuntimeSpec/Receipts), and `Chunk Header`. - **transactions**: `Transaction` is the main way of interaction between a user and a blockchain. Transaction contains: Signer account ID, Receiver account ID, and Actions. - **execution_outcomes**: Execution outcome is the result of execution of `Transaction` or `Receipt`. In the result of the Transaction execution will always be a Receipt. - **receipt_details**: All cross-contract (we assume that each account lives in its own shard) communication in Near happens through Receipts. Receipts are stateful in a sense that they serve not only as messages between accounts but also can be stored in the account storage to await `DataReceipts`. Each receipt has a `predecessor_id` (who sent it) and `receiver_id` the current account. - **receipt_origin**: Tracks the transaction that originated the receipt. - **receipt_actions**: Action Receipt represents a request to apply actions on the `receiver_id` side. It could be derived as a result of a `Transaction` execution or another `ACTION` Receipt processing. Action kind can be: `ADD_KEY`, `CREATE_ACCOUNT`, `DELEGATE_ACTION`, `DELETE_ACCOUNT`, `DELETE_KEY`, `DEPLOY_CONTRACT`, `FUNCTION_CALL`, `STAKE`, `TRANSFER`. - **receipts (view)**: It's recommended to select only the columns and partitions (`block_date`) needed to avoid unnecessary query costs. This view join the receipt details, the transaction that originated the receipt and the receipt execution outcome. - **account_changes**: Each account has an associated state where it stores its metadata and all the contract-related data (contract's code + storage). :::info Additional information about the data - Skipped Blocks: NEAR Blockchain can contain skipped blocks, e.g. block `57730443`. For these cases we can find the block for the chunk data using the `prev_block_hash` column, e.g. `SELECT * FROM chunks c JOIN blocks b ON c.chunk.header.prev_block_hash = b.header.prev_hash`. ::: :::note References - [Protocol documentation](../protocol/basics.md) - [Near Data flow](../protocol/data-flow/near-data-flow.md) - [Protocol specification](https://nomicon.io/) ::: --- # Source: https://docs.near.org/api/rpc/block-chunk.md --- id: block-chunk title: Block / Chunk description: Learn how to retrieve details about blocks and chunks from the RPC hide_table_of_contents: true --- The RPC API enables you to query the network and get details about specific blocks or chunks. ## Quick Reference {#quick-reference} Here's a quick reference table for all the methods in this section: | Method | Description | Parameters | |--------|-------------|------------| | [`block`](#block-details) | Get block details by height, hash, or finality | `finality` OR `block_id` | | [`block_effects`](#block-effects) | Get changes in a specific block | `finality` OR `block_id` | | [`chunk`](#chunk-details) | Get chunk details by chunk_id or block_id + shard_id | `chunk_id` OR [`block_id`, `shard_id`] | ## Block details {#block-details} Queries network and returns block for given height or hash. You can also use `finality` param to return latest block details. **Note**: You may choose to search by a specific block _or_ finality, you can not choose both. - **method**: `block` - **params**: - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) ### finality ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "block", "params": { "finality": "final" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.block({ finality: 'final', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=block \ params:='{ "finality": "final" }' ``` }> ### block height ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "block", "params": { "block_id": 187310138 } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.block({ blockId: 187310138, }); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=block \ params:='{ "block_id": 187310138 }' ``` }> ### block hash ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "block", "params": { "block_id": "6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.block({ blockId: '6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w', }); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=block \ params:='{ "block_id": "6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w" }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "author": "node2", "chunks": [ { "balance_burnt": "0", "bandwidth_requests": null, "chunk_hash": "CzPafxtJmM1FnRoasKWAVhceJzZzkz9RKUBQQ4kY9V1v", "congestion_info": { "allowed_shard": 1, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 308, "encoded_merkle_root": "6z9JwwtVfS5nRKcKeJxgzThRRs2wCNvbH88T3cuARe6W", "gas_limit": 1000000000000000, "gas_used": 0, "height_created": 187310138, "height_included": 187310138, "outcome_root": "11111111111111111111111111111111", "outgoing_receipts_root": "AChfy3dXeJjgD2w5zXkUTFb6w8kg3AYGnyyjsvc7hXLv", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "cRMk2zd2bWC1oBfGowgMTpqW9L5SNG2FeE72yT1wpQA", "rent_paid": "0", "shard_id": 0, "signature": "ed25519:L1iCopW8gY5rqwfuZT8Y3bHHXvuvWT87X9rwdY6LmFi8LGZdMhj2CkQCXLGrzdfYXD8B54wPTM9TqJAHcKfFDyW", "tx_root": "CMwUsP8q4DTBUYxXm12jVwC8xTD8L1T1n3jdKLQVh6bm", "validator_proposals": [], "validator_reward": "0" }, { "balance_burnt": "0", "bandwidth_requests": null, "chunk_hash": "44MZBWmPgXszAyojsffzozvNEdRsJcsq7RrdAV4Y7CLm", "congestion_info": { "allowed_shard": 2, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 8, "encoded_merkle_root": "5TxYudsfZd2FZoMyJEZAP19ASov2ZD43N8ZWv8mKzWgx", "gas_limit": 1000000000000000, "gas_used": 0, "height_created": 187310138, "height_included": 187310138, "outcome_root": "11111111111111111111111111111111", "outgoing_receipts_root": "AChfy3dXeJjgD2w5zXkUTFb6w8kg3AYGnyyjsvc7hXLv", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "EQ5mcUAzJA4du33f9g9YzKvdte2ukyRHMMHbbqdazZvU", "rent_paid": "0", "shard_id": 1, "signature": "ed25519:4ktZTtEfxXSXPVj6Kii52d2T684HKKtEMzrd3dNc7UyxmgkKcLtxD1fawtbj8KsmjbZPGj8YMzanDeViEhxRJtDX", "tx_root": "11111111111111111111111111111111", "validator_proposals": [], "validator_reward": "0" }, { "balance_burnt": "38449649514500000000", "bandwidth_requests": null, "chunk_hash": "7eB8V8zMmNp9GxfRt3oHA3DS7YTgPvZ761pBzoziLay8", "congestion_info": { "allowed_shard": 3, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 1804, "encoded_merkle_root": "6aZKpB3jZbhAq3kDtXaM6s1hYRLYEM624yiKkKvd957m", "gas_limit": 1000000000000000, "gas_used": 384496495145, "height_created": 187310138, "height_included": 187310138, "outcome_root": "D7ojhJ8UAgWf8A51Ekcundn3Kzdc577p5LFxqxcZurdB", "outgoing_receipts_root": "3CK2q73iJmWa36EbaceqGcTz7pD7pia8BsUDE3gixwnF", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "7bSk4ev8EhZFSjb8Zb6ftkEZAUYQdbyPPz2ZkrvjLPuK", "rent_paid": "0", "shard_id": 2, "signature": "ed25519:2sQ4JfYSMFcwpjbmonk67mMCMvuQyCNzvvk3iqCLMR7mnHauy3i7aTbySXwoqnrDjdmNjQ3gJMaA53LSRxYmoyAD", "tx_root": "11111111111111111111111111111111", "validator_proposals": [], "validator_reward": "0" }, { "balance_burnt": "0", "bandwidth_requests": null, "chunk_hash": "9pTjB74BgVSoP4Wb68BjkgnyABvZQUzAvv54YiVgse1B", "congestion_info": { "allowed_shard": 4, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 8, "encoded_merkle_root": "5TxYudsfZd2FZoMyJEZAP19ASov2ZD43N8ZWv8mKzWgx", "gas_limit": 1000000000000000, "gas_used": 0, "height_created": 187310138, "height_included": 187310138, "outcome_root": "11111111111111111111111111111111", "outgoing_receipts_root": "AChfy3dXeJjgD2w5zXkUTFb6w8kg3AYGnyyjsvc7hXLv", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "JDHeuYmX4kvsTPKyJYtJTrm7UK3JHTf4rw7hcHXYEfCn", "rent_paid": "0", "shard_id": 3, "signature": "ed25519:5AejTPwZGWqdZjGCUbhLCcgasNDtsYKRJhS33uYR5Psu6NcCiaeLZnV8Q7dtWK4hLJ1iA48DA2WeqEeUyGhqWAGT", "tx_root": "11111111111111111111111111111111", "validator_proposals": [], "validator_reward": "0" }, { "balance_burnt": "32741908829000000000", "bandwidth_requests": null, "chunk_hash": "2xQwSvBiCb1mkoPxBJhSRg7pjmnrmKMEffatDz73Y8Jj", "congestion_info": { "allowed_shard": 5, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 1042, "encoded_merkle_root": "5XpktxfgMp6thB2nH3PxdSg3K84p2wmpihHxUCqeQA6c", "gas_limit": 1000000000000000, "gas_used": 327419088290, "height_created": 187310138, "height_included": 187310138, "outcome_root": "69ZXwcYi41NY6cx1rZog8YavBPQvN75pmkNHZsFjWfUW", "outgoing_receipts_root": "FqGVK8H8x2P3BbvuFMo7VCTy8cCNTzT1jd5JoLXfYRNG", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "BJePbZUt8VzJBwKf1j1sRUJJJEx4D2fUu4SCHiWs331o", "rent_paid": "0", "shard_id": 4, "signature": "ed25519:2QSda4eMn25hmmTY31wN6RnBpBLjamSLrQRoVZ1yEoWyhtMhtg8rUv9Ko1tEdSftwhNEhL1ETixaAz4qcmvHUvD1", "tx_root": "6qbqA8B9oyeVG33JXH25xbA2DiqvHRnxipurYUBJ9D8B", "validator_proposals": [], "validator_reward": "0" }, { "balance_burnt": "0", "bandwidth_requests": null, "chunk_hash": "EVkgySRKpB9HrEJz8f18p9pWmJzhtL9WeYMwDSeY1827", "congestion_info": { "allowed_shard": 0, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 8, "encoded_merkle_root": "5TxYudsfZd2FZoMyJEZAP19ASov2ZD43N8ZWv8mKzWgx", "gas_limit": 1000000000000000, "gas_used": 0, "height_created": 187310138, "height_included": 187310138, "outcome_root": "11111111111111111111111111111111", "outgoing_receipts_root": "AChfy3dXeJjgD2w5zXkUTFb6w8kg3AYGnyyjsvc7hXLv", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "GkcYxyvnet4nvL7LFdbKxiscFBpe5WSzy5g2pW58LSRs", "rent_paid": "0", "shard_id": 5, "signature": "ed25519:573WUDx8Sm4Fi16PFQkELXYq2SYezcbQP4CuhseqNizDtSTf2c2TKMWf2ZuACiWCSa8ARw8eWB2ZKWaY1uy7xd14", "tx_root": "11111111111111111111111111111111", "validator_proposals": [], "validator_reward": "0" } ], "header": { "approvals": [ null, "ed25519:5GhoQTPXsWpgGPq2ZHZCfP9iY9GSmHMNsnydzxBxnibGvC43PFUAD58aUSNyfepRY4dAMbjbf8CduMyQU83HBxAt", "ed25519:3Vc7sgrrgvpFXRr94mx3CD32Std1MRprR7igChZUisJvUm6f2yJmUGaXk38CYbh2wT1gfsKJ2UHf9icRome3jFYw", "ed25519:3DZqMDGrk3eNUcZ8FiTtdw2piMXVcmVDDs89kHdRdDDgTr88GnQPEym4kfX9FUE81MnbytmotPry2sXD6MvbVprp", "ed25519:5qtN8dU2iCtZxqPNszhPJ2Rgio88QL2wseLPhLH5Ev56WuxcNsmFZbNREvA6cKAKz4aDwQFFmmj888h6EEZuS2TV", "ed25519:3gKUWzXU7Am5xdZqgMN3TC5wGVrf1kp6WQoqqtLFaF9JtBQKqqpZ67CKBm6KfejHPhiRkp1PbDJbppiNCpN3spr6", "ed25519:3wgeF9tcjx1vX2bLpXfm8fYUQnai524XjTNb3Wt2LHMZnNoXzW4D2XBynj8sK41H5wtSbeVYpN3vGY1r23y56BaY", "ed25519:ek5uvofwn5ZJidjYwiqS8Xpd1Y521FAUWrPyU9w3F8UT9yhviWuzPBCJUyMFVKnYUW6k6tZSZxT3NZibGfrGWA4", "ed25519:4UcHZyKNzGDsMSNfQjLA9EE8yXiq3aroLUz4WJATzkKkUfMNVRkWxGc5tih2jDwzKfY3Ni5YiPoBbPzPCxNrusLf", "ed25519:2hXaGXtAxngCjEWvnUUEgnnPCsXYzPzDadGHdw9sz6ng5oDQDPhnTm1MCG37xvv7xgCVhj3tqRZy8v74uap7WFC5", "ed25519:kLuyqcTUynL1P77uMaaRs2MzxiaE1uyVGPjAVxpufK9A19G6LDUfK2GcbFXkqCgvKBJEGZKFPUbBqs7EmDdLPD7", "ed25519:5ebABgQGk7idMAQgiEgc9a78v6fsD1nKXfevdBRJPFCn8bRuuFpthzzCp3NQXcr2XgSpNo6HJp8EZzzZHkSLfTDc", "ed25519:4hTb2qFydXaiMKfv1pCxU4S9TQYQTHhqUPuGy8dejqxt2FFHD2sdFsYCv8Mf8qWRSob77QMuQbj37aQfEuJR2hH4", null, "ed25519:65jkXVzQ8pGsRDApBvVFx4xR7j4gruJaL2wumRHEHWib61M5Ztvtt7TTkz2DMN1nrRy6C7Pfhe3U3KpdSVEKKYAN", null, "ed25519:3kNRvMnpP8t4D9Dgs1YDAXyPNg7V3fpNa3GEWyCNNq5EvXxnyEoXRyPtbrZQM2FbapKsL2DnaNGvewHwBRQz5DbD", "ed25519:5JQugH24LahiK7sn85akbprCtpAWfnZ2ffazxQ61kt9f6pe8b8s2LrHVKV3Wf4Sg3xHuP7fuUZJRyA4MWz3EQ83i", "ed25519:2zXba2vAyGEq1fWauB3Kj6HbExTZG1S9KXKe6xcLxEpJHP9JHE6P3mJzpc765WpNsP21evNGj6mffJAwRHtwRny4", null ], "block_body_hash": "6oSbpNUWcAUuaKWx79qTwyRPDLukg9hZ1RCa2PS5rcGt", "block_merkle_root": "DWK6gpunDXHgxU1KJi3Dx8o2HcKqQmUQJEaisK4M3ovD", "block_ordinal": 139413603, "challenges_result": [], "challenges_root": "11111111111111111111111111111111", "chunk_endorsements": [ [255, 255], [251, 127], [255, 31], [255, 247], [255, 239, 1], [255, 63] ], "chunk_headers_root": "4MjChqi5JChDhaiU4zkhN1jeygZiMd66KeHe3Gz9Vs7s", "chunk_mask": [true, true, true, true, true, true], "chunk_receipts_root": "7nEtD9XsDbRJy7MwvUg4QX5zDUktiEVRP9nM6hHpsHmX", "chunk_tx_root": "44YKYmcG1JTocmPSMGpriLwN8CTi29sD8z5FcocMZAKo", "chunks_included": 6, "epoch_id": "HkFsp3sn9K3KDWVoWPCfUSQocgf5bH4icgjHijePc2aX", "epoch_sync_data_hash": null, "gas_price": "100000000", "hash": "6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w", "height": 187310138, "last_ds_final_block": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "last_final_block": "71qgTQCVFfjQkimSdnhxR8iWSP6o9jqumLcZ9k5g25mT", "latest_protocol_version": 73, "next_bp_hash": "AWcwcDPWUjcW9zGiAt7UEUZzZ5Ue77537turbvBLbsiB", "next_epoch_id": "FQBXgdi9oWKanYBXPP1sNUD93KMquocjT5mVrjQ4PH7E", "outcome_root": "7Qkowo41AoiMdNfyiT83DwvwyReMeqhrkpqTzGm4Z19T", "prev_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_height": 187310137, "prev_state_root": "AiApSbMNq9kPPEiLLWhFpSrX5HoPToaBXztM9fePX2ap", "random_value": "Br6a6tgEhNBZm9iPtxCLhwqwCr2eoEAGGMeVYZnU6fVF", "rent_paid": "0", "signature": "ed25519:YSuWifP5B3VBPuEVJppWt13AShXsWZ64Qus8uHmtddE2mY6u4jnZVv6Gz4tFvWXfBAkZDk5xtd95rUterEdQm5t", "timestamp": 1739254177539033760, "timestamp_nanosec": "1739254177539033760", "total_supply": "2515615267787707740507051994761921", "validator_proposals": [], "validator_reward": "0" } }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Block Effects {#block-effects} Returns changes in block for given block height or hash over all transactions for all the types. Includes changes like `account_touched`, `access_key_touched`, `data_touched`, `contract_code_touched`. You can also use `finality` param to return latest block details. **Note**: You may choose to search by a specific block _or_ finality, you can not choose both. - **method**: `block_effects` - **params**: - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) ### block_effects by finality ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "block_effects", "params": { "finality": "final" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.blockChanges({ finality: 'final', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=block_effects \ params:='{ "finality": "final" }' ``` }> ### using block height ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "block_effects", "params": { "block_id": 187310138 } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.blockChanges({ blockId: 187310138, }); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=block_effects \ params:='{ "block_id": 187310138 }' ``` }> ### using block hash ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "block_effects", "params": { "block_id": "6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.blockChanges({ blockId: '6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w', }); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=block_effects \ params:='{ "block_id": "6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w" }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "block_hash": "6RWmTYhXCzjMjoY3Mz1rfFcnBm8E6XeDDbFEPUA4sv1w", "block_effects": [ { "account_id": "account.rpc-examples.testnet", "type": "account_touched" }, { "account_id": "dev2-nsp.testnet", "type": "account_touched" }, { "account_id": "ping-account.testnet", "type": "account_touched" }, { "account_id": "v1.signer-dev.testnet", "type": "account_touched" }, { "account_id": "account.rpc-examples.testnet", "type": "access_key_touched" }, { "account_id": "ping-account.testnet", "type": "access_key_touched" }, { "account_id": "dev2-nsp.testnet", "type": "data_touched" }, { "account_id": "dev2-nsp.testnet", "type": "data_touched" }, { "account_id": "v1.signer-dev.testnet", "type": "data_touched" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Chunk Details {#chunk-details} Returns details of a specific chunk. You can run a [block details](/api/rpc/block-chunk#block-details) query to get a valid chunk hash. - **method**: `chunk` - **params**: - `chunk_id` _OR_ [`block_id`, `shard_id`](/api/rpc/setup#using-block_id-param) ### chunk_id example ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "chunk", "params": { "chunk_id": "CzPafxtJmM1FnRoasKWAVhceJzZzkz9RKUBQQ4kY9V1v" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.chunk( 'CzPafxtJmM1FnRoasKWAVhceJzZzkz9RKUBQQ4kY9V1v', ); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=chunk \ params:='{ "chunk_id": "CzPafxtJmM1FnRoasKWAVhceJzZzkz9RKUBQQ4kY9V1v" }' ``` }> ### block_id and shard_id example ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "chunk", "params": { "block_id": 187310138, "shard_id": 0 } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.chunk([187310138, 0]); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=chunk \ params:='{ "block_id": 187310138, "shard_id": 0 }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "author": "kiln.pool.f863973.m0", "header": { "balance_burnt": "0", "bandwidth_requests": null, "chunk_hash": "CzPafxtJmM1FnRoasKWAVhceJzZzkz9RKUBQQ4kY9V1v", "congestion_info": { "allowed_shard": 1, "buffered_receipts_gas": "0", "delayed_receipts_gas": "0", "receipt_bytes": 0 }, "encoded_length": 308, "encoded_merkle_root": "6z9JwwtVfS5nRKcKeJxgzThRRs2wCNvbH88T3cuARe6W", "gas_limit": 1000000000000000, "gas_used": 0, "height_created": 187310138, "height_included": 187310138, "outcome_root": "11111111111111111111111111111111", "outgoing_receipts_root": "AChfy3dXeJjgD2w5zXkUTFb6w8kg3AYGnyyjsvc7hXLv", "prev_block_hash": "Wj6B3RTv73EWDNbSammRDeA9315RaPyRrJYmiP4nG4X", "prev_state_root": "cRMk2zd2bWC1oBfGowgMTpqW9L5SNG2FeE72yT1wpQA", "rent_paid": "0", "shard_id": 0, "signature": "ed25519:L1iCopW8gY5rqwfuZT8Y3bHHXvuvWT87X9rwdY6LmFi8LGZdMhj2CkQCXLGrzdfYXD8B54wPTM9TqJAHcKfFDyW", "tx_root": "CMwUsP8q4DTBUYxXm12jVwC8xTD8L1T1n3jdKLQVh6bm", "validator_proposals": [], "validator_reward": "0" }, "receipts": [], "transactions": [ { "actions": [ { "FunctionCall": { "args": "eyJyZWNvcmRfaWQiOjEsInJlY29yZCI6IkhlbGxvLCBOZWFyIFByb3RvY29sISJ9", "deposit": "0", "gas": 50000000000000, "method_name": "write_record" } } ], "hash": "J3KbUXF9YPu2eGnbDCACxGvmMDZMdP7acGYhVLHGu9y2", "nonce": 187309654000001, "priority_fee": 0, "public_key": "ed25519:EddTahJwZpJjYPPmat7DBm1m2vdrFBzVv7e3T4hzkENd", "receiver_id": "contract.rpc-examples.testnet", "signature": "ed25519:3opUQgg5eNQmE2LJ8zJiitBAVLDFR3svk8LC5VtVGorQuq8jWLocKAt7B4xb6n7DhH8zSVCWcRRrmVL9f1wHiVXa", "signer_id": "account.rpc-examples.testnet" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Error Handling {#error-handling} ### Common Error Types | Error Code | Description | Solution | |------------|-------------|----------| | `UNKNOWN_BLOCK` | Block not found or garbage-collected | Check block validity; use archival node for old blocks | | `UNKNOWN_CHUNK` | Chunk not found in database | Verify chunk ID; use archival node for old chunks | | `INVALID_SHARD_ID` | Shard ID does not exist | Provide valid shard ID for existing shard | | `NOT_SYNCED_YET` | Node still syncing | Wait for sync completion or use different node | | `PARSE_ERROR` | Invalid request parameters | Check parameter format and completeness | | `INTERNAL_ERROR` | Server-side issue | Retry request or try different RPC endpoint | ### Response Validation - **Block responses**: Always include `block_hash`, `block_height`, and `header` fields - **Chunk responses**: Contain `author`, `header`, `receipts`, and `transactions` arrays - **Changes responses**: Include `block_hash` and `block_effects` array with change details --- ## Best Practices - **Cache block data**: Block information is immutable once finalized, ideal for caching --- # Source: https://docs.near.org/smart-contracts/security/callbacks.md --- id: callbacks title: Cross-Contract Calls description: "Learn about callback security in NEAR smart contracts, including proper error handling, state management, and preventing callback-related vulnerabilities." --- In NEAR, smart contracts can call each other. This is a powerful feature that allows you to build complex applications by composing smaller contracts. However, it also introduces some security considerations that you need to be aware of. While writing cross-contract calls there is a significant aspect to keep in mind: all the calls are **independent** and **asynchronous**. In other words: - The method in which you make the call and method for the callback are **independent**. - Between the call and the callback, people could interact with the contract. This has important implications on how you should handle the callbacks. Particularly: 1. Your callback method needs to be public, but you want to make sure only your contract can call it. 2. Make sure you don't leave the contract in a exploitable state between the call and the callback. 3. Manually rollback any changes to the state in the callback if the external call failed. --- ## Private Callbacks In order for your contract to call itself when a cross-contract call is done, you need to make the callback method public. However, most of the times you would want it to be private. You can make it private while keeping it public by asserting that the `predecessor` is `current_account`. In rust this is done automatically by adding the `#[private]` decorator. --- ## User's Money When a method panics, the money attached to that transaction returns to the `predecessor`. This means that, if you make a cross-contract call and it fails, then the money **returns to your contract**. If the money came from a user calling your contract, then you should transfer it back during the callback. ![img](https://miro.medium.com/max/1400/1*Hp4TOcaBqm9LS0wkgWw3nA.png) *If the user attached money, we need to manually return it in the callback* :::caution Make sure you pass have enough GAS in the callback to make the transfer ::: --- ## Async Callbacks Between a cross-contract call and its callback **any method of your contract can be executed**. Not taking this into account is one of the main sources of exploits. It is so common that it has its own name: reentrancy attacks. Imagine that we develop a `deposit_and_stake` with the following **wrong logic**: (1) The user sends us money, (2) we add it to its balance, (3) we try to stake it in a validator, (4) if the staking fails, we remove the balance in the callback. Then, a user could schedule a call to withdraw between (2) and (4), and, if the staking failed, we would send money twice to the user. ![img](https://miro.medium.com/max/1400/1*VweWHQYGLBa70uceiWHLQA.png) *Between a cross-contract call and the callback anything could happen* Luckily for us the solution is rather simple. Instead of immediately adding the money to our user’s balance, we wait until the callback. There we check, and if the staking went well, then we add it to their balance. ![img](https://miro.medium.com/max/1400/1*o0YVDCp_7l-L3njJMGhU4w.png) *Correct way to handle deposits in a cross-contract call* --- # Source: https://docs.near.org/chain-abstraction/chain-signatures.md --- id: chain-signatures title: What are Chain Signatures? sidebar_label: Chain Signatures description: "Learn how Chain Signatures enable NEAR accounts to sign and execute transactions across multiple blockchains using Multi-Party Computation for secure cross-chain operations." --- Chain signatures enable NEAR accounts, including smart contracts, to sign and execute transactions across many blockchain protocols. This unlocks the next level of blockchain interoperability by giving ownership of diverse assets, cross-chain accounts, and data to every single NEAR account. ![chain-signatures](/assets/docs/welcome-pages/chain-signatures-overview.png) _Diagram of a chain signature in NEAR_
Supported Networks While you can sign transactions for any network using Eddsa or Ecdsa keys, each chain signs transactions differently. Our example [implementation](./chain-signatures/implementation) shows you how to sign transactions for: Bitcoin, Solana, Cosmos, XRP, Aptos, Sui and EVM networks (Ethereum, Base, BNB Chain, Avalanche, Polygon, Arbitrum, and more).
## Benefits Integration with Chain Signatures brings many benefits to Web3 developers: - Single Account, Multi-Chain Operations: Developers can manage interactions with external blockchains from one NEAR account. This simplifies key management and reduces the need for multiple wallets or addresses, enhancing user experience and security. - Reduced Overhead in Cross-Chain Development: Chain Signatures eliminate the complexity of managing transactions across different blockchains. Developers can write smart contracts on NEAR that directly sign for cross-chain transactions, cutting down on code redundancy and potential points of failure. - Secure Transaction Signing: Using [Multi-Party Computation (MPC)](#multi-party-computation-service), developers gain access to a decentralized signing process for multi-chain transactions. This means no single entity controls the signing key, reducing risks associated with centralized custodianship. :::tip Keep in mind that Chain Signatures is a “one way” solution to sign and execute outbound transactions happening on other blockchains. If you're looking to access states on external blockchains, you should check out Omnibridge. ::: ## Interoperability NEAR's chain abstraction stack allows developers to leverage powerful interoperability options across different blockchains. Developers can execute their business logic and deploy smart contracts on NEAR’s scalable blockchain infrastructure, while maintaining control over external accounts (such as Bitcoin, Ethereum, Base) and assets natively. This integrated approach combines external blockchain assets with NEAR’s scalability, enabling the development of native dApps that offer superior performance and an optimized user experience. By combining the strengths of different ecosystems, NEAR Protocol unlocks an array of transformative possibilities without compromising on decentralization or scalability. ## Use Cases The Chain Signatures architecture provides a decentralized method to interact with multiple blockchains from one NEAR account. With Chain Signatures, blockchain use cases expands to new levels. ### Bitcoin DeFi Developers can build decentralized finance (DeFi) applications on NEAR, such as decentralized exchanges (DEXs), lending platforms, or yield farming protocols, while directly leveraging Bitcoin liquidity. The business logic resides on NEAR, while BTC is used for actual payments. #### Examples - Atomic Swaps: Facilitate trustless, instant exchanges between Bitcoin and other cryptocurrencies, enhancing liquidity and reducing counterparty risk. - Receive Bitcoin payments with a native transfer to a Chain Signature derived account. - Use Chain Signatures to control the payment flow and execute Bitcoin transactions, such as locking or transferring assets. - Use smart contracts on NEAR to encapsulate business logic such as interest calculations, borrowing, order processing, reward distribution, and repayments. ### Cross-Chain NFT Platforms Developers can create a NFT marketplace on NEAR where users purchase NFTs using external cryptocurrencies such as Bitcoin. The marketplace could handle: - BTC payments via Chain Signatures and Omnibridge. - NFT minting and trading logic on NEAR. (_NFTs could also be minted on multiple blockchains thanks to Chain Signatures_) --- ## How It Works Controlling accounts and their assets on other blockchain platforms is made possible thanks to the interaction between three elements: 1. [**Derivation Paths**](#derivation-paths-one-account-multiple-chains) - A deterministic way to derive foreign addresses from one NEAR account 2. [**Multichain Smart Contract**](#multichain-smart-contract) - Receives requests to sign a transaction for other blockchains 3. [**Multiparty Computation Service**](#multi-party-computation-service) - Third-party service providing signatures to the contract ![Chain Signatures](/assets/docs/chain-abstraction/chain-abstract-2.svg) _Chain signatures flow_
### Derivation Paths: One Account, Multiple Chains Chain Signatures link NEAR accounts to addresses in other blockchain using [Additive Key Derivation](https://eprint.iacr.org/2021/1330) (a simple mechanism for deriving many subkeys from a single master key). These keys are generated using `derivation paths` (or `paths` for short). A `derivation path` is simply a string (e.g. `ethereum-1`, `ethereum-2`, etc) that in conjunction with the NEAR account derives a unique address on the target blockchain. For example, we can derive multiple Ethereum addresses from `example.near` by using different paths: 1. `example.near` + `ethereum-1` = `0x1b48b83a308ea4beb845db088180dc3389f8aa3b` 2. `example.near` + `ethereum-2` = `0x99c5d3025dc736541f2d97c3ef3c90de4d221315` 3. `example.near` + `...` = `0x...` It is important to note that this allows us to discover the **public address** of the foreign account that we can control. To actually control the foreign account, we need to request signatures from the MPC service. :::tip In practice, the external address is deterministically derived using the NEAR address (`example.near`), the path (`ethereum-1`) and the MPC service's public key :::
### Multichain Smart Contract A deployed multichain smart contract ([v1.signer](https://nearblocks.io/address/v1.signer)) is used to request signatures for transactions on other blockchains. This contract has [a `sign` method](https://github.com/near/mpc/blob/01f33ed0a2a2c4c24ef49a2f36df3b20aa400816/libs/chain-signatures/contract/src/lib.rs#L242) that takes these three parameters: 1. The `payload` (transaction or transaction hash) to be signed for the target blockchain. 2. The `path` that identifies the account to be used to sign the transaction. 3. The `domain_id` as an integer that identifies the signature scheme to be used for generating the signature. Currently this can be `0` for Secp256k1 or `1` for Ed25519. For example, a user could request a signature to `send 0.1 ETH to 0x060f1...` **(transaction)** using the `ethereum-1` account **(path)** with `0` (Secp256k1) as **domain ID**. After a request is made, the `sign` method will [yield execution](/blog/yield-resume) waiting while the [MPC signing service](#multi-party-computation-service) signs the transaction. Once the signature is ready, the contract resumes computation and returns it to the user. This signature is a valid signed transaction that can be readily sent to the target blockchain to be executed. :::tip The `sign` method currently supports both Secp256k1 and Ed25519 signature schemes which enables signing transactions for the vast majority of the well-known blockchains including Bitcoin, Ethereum, Solana, BNB chain, Ton, or Stellar. In the future, the MPC participants can add more signature schemes via the `vote_add_domains` method. :::
### Multi-Party Computation Service The essence of Multi-Party Computation (MPC) is to enable independent parties to perform shared computations on private information without revealing secrets to each other. In practice, this system can be used with blockchain platforms to safely sign a transaction on behalf of a user without ever having to expose a private key. NEAR's MPC service is comprised of several independent nodes, **none of which can sign by itself**, but instead create **signature-shares** that are **aggregated through multiple rounds** to **jointly** sign a transaction. Currently, the service is composed of 8 independent nodes. However the set of participating nodes can be extended with the `vote_new_parameters` method of the `v1.signer` smart contract if enough active nodes vote for it. This service continuously listens for signature requests (i.e. users calling the `sign` method on the `v1.signer` smart contract) and when a call is detected the MPC service: 1. Asks its nodes to jointly derive a signature for the `payload` using the account identified by the `path` 2. Once complete, call the `v1.signer` contract to store the resulting `Signature` :::info A Custom MPC Service Generally, MPC signing services work by sharing a master key, which needs to be re-created each time a node joins or leaves. NEAR's MPC service allows for nodes to safely join and leave, without needing to re-derive a master key. ::: :::tip Want to learn more about the mathematics that enable MPC? [**Check this awesome article**](https://www.zellic.io/blog/mpc-from-scratch/). ::: --- ## Concluding Remarks Chain Signatures are a powerful tool that allows NEAR accounts to control accounts on other blockchains. This is a fundamental step towards enabling true ownership of cross-chain data and assets. For the user, the process is made completely **on chain**, since they only need to make a call to a smart contract and wait for the response. Thanks to `derivation paths`, a single NEAR account can control **multiple accounts** on different blockchains, and thanks to the MPC service, the user can be sure that **nobody but themselves** can request signatures for those accounts. --- # Source: https://docs.near.org/smart-contracts/security/checklist.md --- id: checklist title: ✅ Checklist description: "Best practices for security and common safeguards." --- Once you finished developing your smart contract please go through the following list in order to ensure everything is safe for the end user. :::info Check our [security articles](./welcome.md) to understand how to improve the security of your contract. ::: --- ## Anatomy 1. All private methods are decorated as `private`. ## Environment 2. `predecessor` and `signer` are used correctly through the entire contract. ## Storage 3. Each time the state grows it is ensured that there is enough balance to cover it 4. All collections (i.e. Vector, Map, Tree, etc) have a unique id 5. Check for underflow and overflow!. In rust, you can do this by simply adding the `overflow-checks = true` flag in your `Cargo.toml`. ## Actions 6. When sending money, you leave enough in the contract to cover the storage cost 7. If you are tracking user's fund, you **deduct them before** sending them back to the user. ## Callbacks 8. All private callbacks are marked as `private` 9. All cross-contract calls have a callback 10. All callbacks check for errors and roll back the state if necessary 11. All callbacks return money to the `predecessor` if necessary 12. Callbacks are free of `panic!` 13. All the callbacks are given enough GAS to execute entirely 14. The contract is not left in an exploitable state between a cross-contract call and its callback --- # Source: https://docs.near.org/tools/clear-state.md --- id: clear-state title: Clear Contract State description: "Clean up a contract's state." --- This simple command-line tool allows you to clean up the state of a NEAR account without deleting it. ## How it works This JavaScript CLI tool deploys a [`state-cleanup.wasm`](https://github.com/near-examples/near-clear-state/blob/main/contractWasm/state_cleanup.wasm) contract replacing the current one, and then uses the new contract to clean up the account's state, so you can easily redeploy a new contract or use the account in any other way. Here's a quick snippet of the contract's main code: ``` let input = env::input().unwrap(); let args: Args = serde_json::from_slice(&input).unwrap(); for key in args.keys.iter() { env::storage_remove(&base64::decode(key).unwrap()); } ``` :::tip Want to check the smart contract? Check the GitHub repository and learn more about the [State Cleanup tool](https://github.com/near-examples/near-clear-state). ::: --- ## How to use ### Requirements You'll need [NEAR CLI](cli.md). You can install it by running: ```bash npm install -g near-cli-rs@latest ``` ### Clear your Account State To clear your Account state, follow these steps. #### 1. Login with NEAR CLI This will store a full access key locally on your machine. Select the account you wish to clear the state. ```bash near login ``` :::warning Legacy keychain Be sure to select `Store the access key in my legacy keychain (compatible with the old near CLI)` to store the access key on the legacy keychain. ::: #### 2. Clone the `near-clear-state` Repository ```sh git clone https://github.com/near-examples/near-clear-state.git ``` #### 3. Install dependencies ```bash cd near-clear-state && npm i ``` #### 4. Clear your State ```bash npx near-clear-state clear-state --account ``` :::tip mainnet If you want to clean the state of a `mainnet` account, use the `--network` option: ```sh npx near-clear-state clear-state --account --network mainnet ``` ::: #### (Optional) Check your results You can view the all the state keys have been erased in your account with: ```bash near view-state ``` --- ## Troubleshooting If your contract state is large, depending on the RPC node, you may get the error: ``` State of contract example.near is too large to be viewed. ``` This is an RPC issue, as the RPC node has a limited contract state view. You can prevent the error `State of contract example.near is too large to be viewed` when calling view-state via the JSON RPC API if you select an alternative RPC provider. You can find different providers in [this RPC list](../api/rpc/providers.md). ``` config = { networkId: netId, keyStore, nodeUrl: "https://rpc."+ netId +".near.org", walletUrl: "https://wallet.testnet.near.org", helperUrl: "https://helper.testnet.near.org", explorerUrl: "https://explorer.testnet.near.org", }; near = await connect(config); ``` For example, you could replace the default RPC node in [`commands/clearState.js`](https://github.com/near-examples/near-clear-state/blob/main/commands/clearState.js) with another RPC server: ```js config = { networkId: netId, keyStore, nodeUrl: "https://endpoints.omniatech.io/v1/near/"+ netId +"/public", ... ``` --- # Source: https://docs.near.org/ai/shade-agents/reference/cli.md --- id: cli title: Shade Agent CLI sidebar_label: Shade Agent CLI description: "Learn about the Shade Agent CLI and how to use it to deploy Shade Agents." --- The [Shade Agent CLI](https://github.com/NearDeFi/shade-agent-cli/tree/main) makes it simple to deploy a Shade Agent. --- ## CLI Overview Under the hood, the CLI: - Builds and publishes the Docker Image for your agent app, and modifies your environment variables and docker-compose.yaml to match your new image hash - Creates the agent contract account, deploys the agent contract to it, and initializes it with the NEAR_ACCOUNT_ID as the owner - Approves the image code hashes for your agent (app and API image) - Deploys the agent to a TEE on Phala Cloud --- ## Local vs Production The `NEXT_PUBLIC_contractId` in your environment variables prefix should be set to `ac-proxy.` for local development and `ac-sandbox.` for TEE deployment. For local deployment, the CLI works a little differently: - No Docker Image is built or published for your agent app - An agent contract that doesn't require agent registration is deployed instead (since locally, you cannot produce a valid TEE attestation) - The API image is hosted locally instead of deploying anything to a TEE --- ## Installation ```bash npm i -g @neardefi/shade-agent-cli ``` --- ## Usage The Shade Agent CLI is a single command that runs the CLI with standard configurations. Run the command within the root of your project. ```bash shade-agent-cli ``` Your project root must contain your `.env.development.local`, `Dockerfile` and `docker-compose.yaml` files. --- ## Flags The CLI includes various flags to configure deployment options and disable specific components. If you require further customizability when deploying your agent, you can disable the relevant components using flags and complete those steps manually with native tools: [Docker CLI](https://docs.docker.com/reference/cli/docker/), [NEAR CLI](https://docs.near.org/tools/near-cli), and [Phala CLI](https://docs.phala.network/phala-cloud/phala-cloud-cli/overview) - **--wasm ``** Path to a custom agent contract WASM file (e.g. `contract/near/contract.wasm`) to deploy instead of the default contract. Use this when deploying [custom contracts](./custom-agent-contract.md). - **--funding ``** Amount of NEAR tokens to fund the contract deployment with (e.g. `5` for 5 NEAR). Use this when deploying a custom contract. - **--image** Build and push the Docker image only. Use this when you want customizability over the deployment of the agent contract. - **--contract** Build and push the Docker image, and deploy the contract only. Use this when you want customizability over the contract initialization and approval of image code hashes. - **--phala-only** Deploy the agent, specified by the current `docker-compose.yaml`, to Phala Cloud. Use this when you want to redeploy the same agent as you last deployed. - **--no-redeploy** Skip redeploying the contract. Use when you want to deploy a new agent quicker and you don't need the agent contract redeploying. - **--no-build** Skip building and pushing the Docker image. Use this when you want to redeploy the same agent or want more control over the image build process. - **--no-phala** Skip deploying the agent to Phala Cloud. Use this when you want more control over hosting the agent (e.g. deploying to a TEE with higher specs). - **--no-cache** Run Docker build with --no-cache. Use this when you need clean builds or want to update cached layers (e.g. cached layers may use older packages). :::note Some options are mutually exclusive. See error messages for details if you use conflicting flags. ::: --- ## Custom RPC To use customs RPCs with the CLI and the API, create a file named `near-rpc.json` within your project's root and configure the RPCs you would like to use, for example: ```json { "nearRpcProviders": [ { "connectionInfo": { "url": "https://neart.lava.build:443" }, "options": { "retries": 3, "backoff": 2, "wait": 1000 } }, { "connectionInfo": { "url": "https://test.rpc.fastnear.com" }, "options": { "retries": 3, "backoff": 2, "wait": 1000 } } ] } ``` If required, you can specify headers under connectionInfo. --- # Source: https://docs.near.org/tutorials/examples/coin-flip.md --- id: coin-flip title: Coin Flip description: "Learn to handle randomness on NEAR." --- 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`. ``` @call({}) flip_coin({ player_guess }: { player_guess: Side }): Side { // Check who called the method const player: AccountId = near.predecessorAccountId(); near.log(`${player} chose ${player_guess}`); // Simulate a Coin Flip const outcome = simulateCoinFlip(); // Get the current player points let player_points: number = this.points.get(player, { defaultValue: 0 }) // Check if their guess was right and modify the points accordingly if (player_guess == outcome) { near.log(`The result was ${outcome}, you get a point!`); player_points += 1; } else { near.log(`The result was ${outcome}, you lost a point`); player_points = player_points ? player_points - 1 : 0; } // Store the new points this.points.set(player, player_points) return outcome } // View how many points a specific player has @view({}) points_of({ player }: { player: AccountId }): number { const points = this.points.get(player, { defaultValue: 0 }) near.log(`Points for ${player}: ${points}`) return points } } ``` ``` pub fn flip_coin(&mut self, player_guess: String) -> String { // Check who called the method let player: AccountId = env::predecessor_account_id(); log_str(&format!("{player} chose {player_guess}")); // Simulate a Coin Flip let outcome = simulate_coin_flip(); // Get the current player points let mut player_points = self.points.get(&player).unwrap_or(0); // Check if their guess was right and modify the points accordingly if outcome.eq(&player_guess) { player_points = player_points + 1; } else { player_points = player_points.saturating_sub(1); }; log_str(&format!("player_points: {player_points}")); // Store the new points self.points.insert(&player, &player_points); return outcome; } ``` ### 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. ``` import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet'; import { setupMeteorWalletApp } from '@near-wallet-selector/meteor-wallet-app'; import { setupBitteWallet } from '@near-wallet-selector/bitte-wallet'; import { setupEthereumWallets } from '@near-wallet-selector/ethereum-wallets'; import { setupHotWallet } from '@near-wallet-selector/hot-wallet'; import { setupLedger } from '@near-wallet-selector/ledger'; import { setupSender } from '@near-wallet-selector/sender'; import { setupHereWallet } from '@near-wallet-selector/here-wallet'; import { setupNearMobileWallet } from '@near-wallet-selector/near-mobile-wallet'; import { setupWelldoneWallet } from '@near-wallet-selector/welldone-wallet'; import { WalletSelectorProvider } from '@near-wallet-selector/react-hook'; import { Navigation } from '@/components/Navigation'; import { NetworkId, CoinFlipContract } from '@/config'; import { wagmiConfig, web3Modal } from '@/wallets/web3modal'; import '@/styles/globals.css'; import '@near-wallet-selector/modal-ui/styles.css'; const walletSelectorConfig = { network: NetworkId, createAccessKeyFor: CoinFlipContract, modules: [ setupEthereumWallets({ wagmiConfig, web3Modal, alwaysOnboardDuringSignIn: true }), setupBitteWallet(), setupMeteorWallet(), setupMeteorWalletApp({contractId: CoinFlipContract}), setupHotWallet(), setupLedger(), setupSender(), setupHereWallet(), setupNearMobileWallet(), setupWelldoneWallet(), setupMyNearWallet(), ], } export default function App({ Component, pageProps }) { return ( ); } ``` ``` import { useEffect, useState } from "react"; import Coin from "@/components/Coin"; import { useWalletSelector } from '@near-wallet-selector/react-hook'; import { CoinFlipContract } from "@/config"; import styles from "@/styles/app.module.css"; export default function Home() { const { signedAccountId, callFunction, viewFunction } = useWalletSelector(); const [side, setSide] = useState(null); const [status, setStatus] = useState("Waiting for user input"); const [points, setPoints] = useState(0); const [choice, setChoice] = useState(); useEffect(() => { if (!signedAccountId) return; viewFunction({ contractId: CoinFlipContract, method: "points_of", args: { player: signedAccountId }, }).then((score) => setPoints(score)) }, [signedAccountId]); const handleChoice = async (guess) => { setStatus("Asking the contract to flip a coin"); setChoice(guess); setSide("loading"); let outcome = await callFunction({ contractId: CoinFlipContract, method: "flip_coin", args: { player_guess: guess }, }); setSide(outcome); setStatus(`The outcome was ${outcome}`); if (guess === outcome) { setStatus("You were right, you won a point!"); setPoints(points + 1); } else { setStatus("You were wrong, you lost a point"); setPoints(points ? points - 1 : 0); } }; let color = choice === side ? "btn-success" : "btn-danger"; return (
{!signedAccountId && (

Welcome! Login to Play

)} {signedAccountId && (

What do you think is coming next?

Status: {status}

Your points so far: {points}

)}
); } ```
--- ## 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). ``` test('by default the user has no points', async (t) => { const { root, contract } = t.context.accounts; const points = await contract.view('points_of', { player: root.accountId }); t.is(points, 0); }); test('the points are correctly computed', async (t) => { const { root, contract } = t.context.accounts; let counter = { 'heads': 0, 'tails': 0 } let expected_points = 0; for(let i=0; i<10; i++){ const res = await root.call(contract, 'flip_coin', { 'player_guess': 'heads' }) counter[res] += 1; expected_points += res == 'heads' ? 1 : -1; expected_points = Math.max(expected_points, 0); } // A binomial(10, 1/2) has a P(x>2) ~ 0.98% t.true(counter['heads'] >= 2); t.true(counter['tails'] >= 2); const points = await contract.view('points_of', { 'player': root.accountId }); t.is(points, expected_points); }); ``` ``` async fn test_user_has_no_points( user: &Account, contract: &Contract, ) -> Result<(), Box> { let points: u8 = user .call(contract.id(), "points_of") .args_json(json!({ "player": user.id()})) .transact() .await? .json()?; assert_eq!(points, 0); println!(" Passed ✅ test_user_has_no_points"); Ok(()) } async fn test_points_are_correctly_computed( user: &Account, contract: &Contract, ) -> Result<(), Box> { let mut tails_counter = 0; let mut heads_counter = 0; let mut expected_points = 0; let mut i = 0; while i < 10 { let outcome: String = user.call(contract.id(), "flip_coin") .args_json(json!({"player_guess": "tails"})) .transact() .await? .json()?; if outcome.eq("tails") { tails_counter = tails_counter + 1; expected_points = expected_points + 1; } else { heads_counter = heads_counter + 1; if expected_points > 0 { expected_points = expected_points - 1; } } i = i + 1; } assert!(heads_counter >= 2); assert!(tails_counter >= 2); let points: u8 = user .call(contract.id(), "points_of") .args_json(json!({ "player": user.id()})) .transact() .await? .json()?; assert_eq!(points, expected_points); println!(" Passed ✅ test_points_are_correctly_computed"); Ok(()) } ``` --- ## 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` ::: --- # Source: https://docs.near.org/smart-contracts/anatomy/collections.md --- id: collections title: Collections description: "Efficiently store, access, and manage data in smart contracts." --- When deciding on data structures it is important to understand their tradeoffs. Choosing the wrong structure can create a bottleneck as the application scales, and migrating the state to the new data structures will come at a cost. You can choose between two types of collections: 1. Native collections (e.g. `Array`, `Map`, `Set`), provided by the language 2. SDK collections (e.g. `IterableMap`, `Vector`), provided by the NEAR SDK :::tip Native vs SDK Collections Use native collections for small amounts of data that need to be accessed altogether, and SDK collections for large amounts of data that do not need to be accessed altogether. If your collection has up to 100 entries, it's acceptable to use the native collection. For larger ones, prefer to use SDK collection. For comparison please refer to [this benchmark](https://www.github.com/volodymyr-matselyukh/near-benchmarking). ::: --- ## Storage Management Each time the contract is executed, the first thing it will do is to read the values and [deserialize](./serialization.md) them into memory, and after the function finishes, it will [serialize](./serialization.md) and write the values back to the database. For native collections, the contract will fully load the collection into memory before any method executes. This happens even if the method you invoke does not use the collection. Know that this will have impact on GAS you spend for methods in your contract.
Storage Cost Your contract needs to lock a portion of their balance proportional to the amount of data they stored in the blockchain. This means that: - If more data is added the **storage increases ↑**, and your contract's **balance decreases ↓**. - If data is deleted the **storage decreases ↓**, and your contract's **balance increases ↑**. Currently, it costs approximately **1 Ⓝ** to store **100kb** of data.
Storage Constraints on NEAR For storing data on-chain it’s important to keep in mind the following: - There is a 4mb limit on how much you can upload at once Let’s say for example, someone wants to put an NFT purely on-chain (rather than IPFS or some other decentralized storage solution) you’ll have almost an unlimited amount of storage but will have to pay 1 $NEAR per 100kb of storage used. Users will be limited to 4MB per contract call upload due to MAX_GAS constraints. The maximum amount of gas one can attach to a given functionCall is 300TGas.
:::caution Your contract will panic if you try to store data but don't have NEAR to cover its storage cost ::: :::danger Be mindful of potential [small deposit attacks](../security/storage.md) ::: --- ## Native Collections Native collections are those provided by the language, such as `Array`, `Map`, `Set` in Javascript, or `Vec`, `HashMap`, `HashSet` in Rust. All entries in a native collection are **serialized into a single value** and **stored together** into the state. This means that every time a function execute, the SDK will read and **deserialize all entries** in the native collection.
Serialization & Storage Example The array `[1,2,3,4]` will be serialized into the JSON string `"[1,2,3,4]"` in Javascript, and the Borsh byte-stream `[0,0,0,4,1,2,3,4]` in Rust before being stored
:::tip When to use them Native collections are useful if you are planning to store smalls amounts of data that need to be accessed all together ::: :::danger Keep Native Collections Small As the native collection grows, deserializing it from memory will cost more and more gas. If the collections grows too large, your contract might expend all the gas trying to read its state, making it fail on each function call ::: --- ## SDK Collections The NEAR SDKs expose collections that are optimized for random access of large amounts of data. SDK collections are instantiated using a "prefix", which is used as an index to split the data into chunks. This way, SDK collections can defer reading and writing to the store until needed.
Serialization & Storage Example The sdk array `[1,2,3,4]` with prefix `"p"` will be stored as the string `"p"` in the contract's attribute, and create four entries in the contract's storage: `p-0:1`, `p-1:2`...
SDK Collections' Features | Type | Iterable | Clear All Values | Preserves Insertion Order | Range Selection | |----------------|:--------:|:----------------:|:-------------------------:|:---------------:| | `Vector` | ✅ | ✅ | ✅ | ✅ | | `LookupSet` | | | | | | `UnorderedSet` | ✅ | ✅ | | ✅ | | `IterableSet` | ✅ | ✅ | | ✅ | | `LookupMap` | | | | | | `UnorderedMap` | ✅ | ✅ | | ✅ | | `IterableMap` | ✅ | ✅ | | ✅ | | `TreeMap` | ✅ | ✅ | ✅ | ✅ |
SDK Collections' Time Complexities | Type | Access | Insert | Delete | Search | Traverse | Clear | |----------------|:------:|:--------:|:--------:|:--------:|:--------:|:-----:| | `Vector` | O(1) | O(1)\* | O(1)\*\* | O(n) | O(n) | O(n) | | `LookupSet` | O(1) | O(1) | O(1) | O(1) | N/A | N/A | | `UnorderedSet` | O(1) | O(1) | O(1) | O(1) | O(n) | O(n) | | `IterableSet` | O(1) | O(1) | O(1) | O(1) | O(n) | O(n) | | `LookupMap` | O(1) | O(1) | O(1) | O(1) | N/A | N/A | | `IterableMap` | O(1) | O(1) | O(1) | O(1) | O(n) | O(n) | | `TreeMap` | O(1) | O(log n) | O(log n) | O(log n) | O(n) | O(n) | _\* - to insert at the end of the vector using `push_back` (or `push_front` for deque)_ _\*\* - to delete from the end of the vector using `pop` (or `pop_front` for deque), or delete using `swap_remove` which swaps the element with the last element of the vector and then removes it._
These collections are built to have an interface similar to native collections. :::tip when to use them SDK collections are useful when you are planning to store large amounts of data that do not need to be accessed all together :::
### Instantiation All structures need to be initialized using a **unique `prefix`**, which will be used to index the collection's values in the account's state ``` // Define the contract structure #[near(contract_state)] pub struct StorageExample { pub vector: Vector, pub lookup_set: LookupSet, pub iterable_set: IterableSet, pub lookup_map: LookupMap, pub iterable_map: IterableMap, pub nested: LookupMap>, } // Define the default, which automatically initializes the contract impl Default for StorageExample { fn default() -> Self { Self { vector: Vector::new(Prefix::Vector), lookup_set: LookupSet::new(Prefix::LookupSet), iterable_set: IterableSet::new(Prefix::IterableSet), lookup_map: LookupMap::new(Prefix::LookupMap), iterable_map: IterableMap::new(Prefix::IterableMap), nested: LookupMap::new(Prefix::Root), } } } ``` :::tip Notice how we use `enums` to ensure all collections have a different prefix. Another advantage of using `enums` is that they are serialized into a single `byte` prefix. ::: ``` static schema = { greeting: "string", big: BigInt, vector: { class: Vector, value: "number" }, lookup_set: { class: LookupSet, value: "number" }, unordered_set: { class: UnorderedSet, value: "number" }, lookup_map: { class: LookupMap, value: "number" }, unordered_map: { class: UnorderedMap, value: "number" }, nested: { class: UnorderedMap, value: { class: UnorderedMap, value: "number" } } }; greeting: string = 'Hello'; big: bigint = BigInt(0); vector: Vector = new Vector('uid-1'); lookup_set: LookupSet = new LookupSet('uid-2'); unordered_set: UnorderedSet = new UnorderedSet('uid-3'); lookup_map: LookupMap = new LookupMap('uid-4'); unordered_map: UnorderedMap = new UnorderedMap('uid-5'); nested: UnorderedMap> = new UnorderedMap>('uid-6'); @view({}) // This method is read-only and can be called for free ``` :::tip Do not forget to use the `schema` to define how your contract's state is structured ::: ```python from near_sdk_py import view, call, init from near_sdk_py.collections import Vector, LookupMap, UnorderedMap, LookupSet, UnorderedSet class MyContract: @init def new(self): # Create a Vector with prefix "v" self.my_vector = Vector("v") # Create a LookupMap with prefix "m" self.my_lookup_map = LookupMap("m") # Create an UnorderedMap with prefix "um" self.my_unordered_map = UnorderedMap("um") # Create a LookupSet with prefix "s" self.my_lookup_set = LookupSet("s") # Create an UnorderedSet with prefix "us" self.my_unordered_set = UnorderedSet("us") # For nested collections, use different prefixes self.nested_maps = UnorderedMap("nested") return True @call def create_nested_map(self, key: str): # Create a new map that will be stored inside another map inner_map = UnorderedMap(f"inner_{key}") self.nested_maps[key] = inner_map return {"success": True} ```` :::danger Be careful of not using the same prefix in two collections, otherwise, their storage space will collide, and you might overwrite information from one collection when writing in the other :::
### Vector Implements a [vector/array](https://en.wikipedia.org/wiki/Array_data_structure) which persists in the contract's storage. Please refer to the Rust and JS SDK's for a full reference on their interfaces. ``` #[near] impl StorageExample { pub fn push(&mut self, value: i32) { self.vector.push(value); } pub fn get(&self, index: u32) -> i32 { self.vector[index] } pub fn replace(&mut self, index: u32, value: i32) { self.vector[index] = value; } pub fn len(&self) -> u32 { self.vector.len() } pub fn iter(&self, from_index: i32, limit: i32) -> Vec<&i32> { self.vector .iter() .skip(from_index as usize) .take(limit as usize) .collect() } } ``` ``` // Vector @call({}) push_vector({ value }: { value: number }) { this.vector.push(value); } @view({}) get_vector({ index }: { index: number }): number { return this.vector.get(index); } @call({}) replace_vector({ index, value }: { index: number, value: number }) { this.vector.replace(index, value); } @view({}) len_vector(): number { return this.vector.length; } @view({}) iter_vector({ from_index, limit }: { from_index: number, limit: number }) { return this.vector.toArray().slice(from_index, limit); } // LookupSet ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import Vector class VectorExample: @init def new(self): # Create a Vector with prefix "v" self.my_vector = Vector("v") @call def add_number(self, number): # Append a value to the vector self.my_vector.append(number) return len(self.my_vector) @view def get_number(self, index): # Get a value at specific index try: return self.my_vector[index] except Exception: return None @view def get_all_numbers(self): # Convert entire vector to a list return [num for num in self.my_vector] ```
### LookupMap Implements a [map/dictionary](https://en.wikipedia.org/wiki/Associative_array) which persists in the contract's storage. Please refer to the Rust and JS SDK's for a full reference on their interfaces. ``` #[near] impl StorageExample { pub fn insert_map(&mut self, key: String, value: i32) { self.lookup_map.insert(key, value); } pub fn remove_map(&mut self, key: String) { self.lookup_map.remove(&key); } pub fn get_map(&self, key: String) -> i32 { self.lookup_map[&key] } pub fn contains_key_map(&self, key: String) -> bool { self.lookup_map.contains_key(&key) } } #[cfg(test)] ``` ``` // LookupMap @call({}) insert_lookup_map({ key, value }: { key: string, value: number }) { this.lookup_map.set(key, value); } @call({}) remove_lookup_map({ key }: { key: string }) { this.lookup_map.remove(key); } @view({}) get_lookup_map({ key }: { key: string }): number { return this.lookup_map.get(key); } @view({}) contains_key_lookup_map({ key }: { key: string }): boolean { return this.lookup_map.containsKey(key); } // UnorderedMap ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import LookupMap class LookupMapExample: @init def new(self): # Create a LookupMap with prefix "m" self.balances = LookupMap("m") @call def set_balance(self, account_id, amount): # Set a value for a key self.balances[account_id] = amount return True @view def get_balance(self, account_id): # Get a value for a key with a default return self.balances.get(account_id, 0) @call def remove_balance(self, account_id): # Remove a key if account_id in self.balances: del self.balances[account_id] return True return False ```
### UnorderedMap / IterableMap Implements a [map/dictionary](https://en.wikipedia.org/wiki/Associative_array) which persists in the contract's storage. Please refer to the Rust and JS SDK's for a full reference on their interfaces. ``` #[near] impl StorageExample { pub fn insert_iterable_map(&mut self, key: String, value: i32) { self.iterable_map.insert(key, value); } pub fn remove_iterable_map(&mut self, key: String) { self.iterable_map.remove(&key); } pub fn get_iterable_map(&self, key: String) -> i32 { self.iterable_map[&key] } pub fn contains_key_iterable_map(&self, key: String) -> bool { self.iterable_map.contains_key(&key) } pub fn iter_iterable_map(&self, from_index: i32, limit: i32) -> Vec<(&String, &i32)> { self.iterable_map .iter() .skip(from_index as usize) .take(limit as usize) .collect() } } ``` ``` // UnorderedMap @call({}) insert_unordered_map({ key, value }: { key: string, value: number }) { this.unordered_map.set(key, value); } @call({}) remove_unordered_map({ key }: { key: string }) { this.unordered_map.remove(key); } @view({}) get_unordered_map({ key }: { key: string }): number { return this.unordered_map.get(key); } @view({}) iter_unordered_map({ from_index, limit }: { from_index: number, limit: number }) { return this.unordered_map.toArray().slice(from_index, limit); } // Nested ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import UnorderedMap class UnorderedMapExample: @init def new(self): # Create an UnorderedMap with prefix "um" self.user_data = UnorderedMap("um") @call def set_user_data(self, account_id, data): # Set a value for a key self.user_data[account_id] = data return True @view def get_user_data(self, account_id): # Get a value for a key try: return self.user_data[account_id] except Exception: return None @view def list_all_users(self): # Iterate through keys and values return { "keys": self.user_data.keys(), "values": self.user_data.values(), "items": self.user_data.items() } ```
### LookupSet Implements a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) which persists in the contract's storage. Please refer to the Rust and JS SDK's for a full reference on their interfaces. ``` #[near] impl StorageExample { pub fn insert_set(&mut self, value: i32) { self.lookup_set.insert(value); } pub fn remove_set(&mut self, value: i32) { self.lookup_set.remove(&value); } pub fn contains_set(&self, value: i32) -> bool { self.lookup_set.contains(&value) } } #[cfg(test)] ``` ``` // LookupSet @call({}) insert_lookup_set({ value }: { value: number }) { this.lookup_set.set(value); } @call({}) remove_lookup_set({ value }: { value: number }) { this.lookup_set.remove(value); } @view({}) contains_lookup_set({ value }: { value: number }): boolean { return this.lookup_set.contains(value); } // UnorderedSet ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import LookupSet class LookupSetExample: @init def new(self): # Create a LookupSet with prefix "s" self.whitelist = LookupSet("s") @call def add_to_whitelist(self, account_id): # Add a value to the set self.whitelist.add(account_id) return True @view def is_whitelisted(self, account_id): # Check if a value exists in the set return account_id in self.whitelist @call def remove_from_whitelist(self, account_id): # Remove a value from the set try: self.whitelist.remove(account_id) return True except Exception: return False ```
### UnorderedSet / IterableSet Implements a [map/dictionary](https://en.wikipedia.org/wiki/Associative_array) which persists in the contract's storage. Please refer to the Rust and JS SDK's for a full reference on their interfaces. ``` #[near] impl StorageExample { pub fn insert_iterable_set(&mut self, value: i32) { self.iterable_set.insert(value); } pub fn remove_iterable_set(&mut self, value: i32) { self.iterable_set.remove(&value); } pub fn contains_iterable_set(&self, value: i32) -> bool { self.iterable_set.contains(&value) } pub fn iter_iterable_set(&self, from_index: i32, limit: i32) -> Vec<&i32> { self.iterable_set .iter() .skip(from_index as usize) .take(limit as usize) .collect() } } #[cfg(test)] ``` ``` // UnorderedSet @call({}) insert_unordered_set({ value }: { value: number }) { this.unordered_set.set(value); } @call({}) remove_unordered_set({ value }: { value: number }) { this.unordered_set.remove(value); } @view({}) contains_unordered_set({ value }: { value: number }): boolean { return this.unordered_set.contains(value); } @view({}) iter_unordered_set({ from_index, limit }: { from_index: number, limit: number }) { return this.unordered_set.toArray().slice(from_index, limit); } // LookupMap ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import UnorderedSet class UnorderedSetExample: @init def new(self): # Create an UnorderedSet with prefix "us" self.owners = UnorderedSet("us") @call def register_owner(self, account_id): # Add a value to the set self.owners.add(account_id) return True @view def is_owner(self, account_id): # Check if a value exists in the set return account_id in self.owners @view def list_all_owners(self): # Get all values as a list return self.owners.values() @call def remove_owner(self, account_id): # Try to remove a value if it exists self.owners.discard(account_id) return True ```
### Tree An ordered equivalent of Map. The underlying implementation is based on an [AVL](https://en.wikipedia.org/wiki/AVL_tree). You should use this structure when you need to: have a consistent order, or access the min/max keys. ``` #[near] impl StorageExample { pub fn insert_tree(&mut self, key: String, value: i32) { self.tree_map.insert(&key, &value); } pub fn remove_tree(&mut self, key: String) { self.tree_map.remove(&key); } pub fn get_tree(&self, key: String) -> i32 { self.tree_map.get(&key).expect("Expected value") } pub fn contains_key_tree(&self, key: String) -> bool { self.tree_map.contains_key(&key) } } #[cfg(test)] ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import TreeMap class TreeMapExample: @init def new(self): # Create a TreeMap with prefix "tm" self.scores = TreeMap("tm") @call def add_score(self, user_id, score): # Set score for a user self.scores[user_id] = score return True @view def get_top_scores(self, limit=10): # Get top scores using ordered keys # This returns highest scores first top_users = [] max_key = self.scores.max_key() current_key = max_key count = 0 while current_key is not None and count < limit: top_users.append({ "user": current_key, "score": self.scores[current_key] }) current_key = self.scores.floor_key(current_key - 1) # Get next highest key count += 1 return top_users ``` --- ## Nesting Collections When nesting SDK collections, be careful to **use different prefixes** for all collections, including the nested ones. ``` #[near] impl StorageExample { pub fn insert_nested(&mut self, key: String, value: i32) { if self.nested.contains_key(&key) { let vector = self.nested.get_mut(&key).expect("Wrong key"); vector.push(value); } else { let mut vector = Vector::new(Prefix::Nested(key.clone())); vector.push(value); self.nested.insert(key, vector); } } pub fn remove_nested(&mut self, key: String) { let vector = self.nested.get_mut(&key).expect("Wrong key"); vector.clear(); self.nested.remove(&key); } pub fn get_nested(&self, key: String) -> Vec<&i32> { self.nested[&key].iter().collect() } pub fn contains_key_nested(&self, key: String) -> bool { self.nested.contains_key(&key) } } ``` :::tip Notice how we use `enums` that take a `String` argument to ensure all collections have a different prefix ::: ``` // Nested @call({}) insert_nested({ key, value }: { key: string, value: number }) { const accountId = near.signerAccountId(); let innerMap = this.nested.get(accountId); if (innerMap === null) { innerMap = new UnorderedMap(accountId); } innerMap.set(key, value); this.nested.set(accountId, innerMap); } @call({}) remove_nested({ key }: { key: string }) { const accountId = near.signerAccountId(); const innerMap = this.nested.get(accountId); innerMap.remove(key); this.nested.set(accountId, innerMap); } @view({}) get_nested({ accountId, key }: { accountId: string, key: string }): number { const innerMap = this.nested.get(accountId); if (innerMap === null) { return null; } return innerMap.get(key); } @view({}) iter_nested({ accountId, from_index, limit }: { accountId: string, from_index: number, limit: number }) { const innerMap = this.nested.get(accountId); if (innerMap === null) { return null; } return innerMap.toArray().slice(from_index, limit); } } ``` ```python from near_sdk_py import view, call, init from near_sdk_py.collections import UnorderedMap, Vector from near_sdk_py.collections import create_prefix_guard class NestedCollectionsExample: @init def new(self): # Main map of users to their assets self.user_assets = UnorderedMap("users") @call def add_asset(self, user_id, asset_id, metadata): # Get or create the user's assets vector with a unique prefix if user_id not in self.user_assets: # Create a prefix guard to ensure unique prefixes for this user prefix = f"assets:{user_id}" # Create a new vector for this user's assets self.user_assets[user_id] = Vector(prefix) # Add the asset to the user's assets vector user_assets = self.user_assets[user_id] user_assets.append({ "asset_id": asset_id, "metadata": metadata }) # Update the vector in the map self.user_assets[user_id] = user_assets return True @view def get_user_assets(self, user_id): if user_id not in self.user_assets: return [] # Get all assets for the user user_assets = self.user_assets[user_id] return [asset for asset in user_assets] ```` :::tip In Python, we create unique prefixes for nested collections by including the parent's identifier in the prefix string. The SDK also provides a `create_prefix_guard` utility to help manage prefixes. ::: --- ## Error prone patterns Because the values are not kept in memory and are lazily loaded from storage, it's important to make sure if a collection is replaced or removed, that the storage is cleared. In addition, it is important that if the collection is modified, the collection itself is updated in state because most collections will store some metadata. Some error-prone patterns to avoid that cannot be restricted at the type level are: ```rust use near_sdk::store::UnorderedMap; let mut m = UnorderedMap::::new(b"m"); m.insert(1, "test".to_string()); assert_eq!(m.len(), 1); assert_eq!(m.get(&1), Some(&"test".to_string())); // Bug 1: Should not replace any collections without clearing state, this will reset any // metadata, such as the number of elements, leading to bugs. If you replace the collection // with something with a different prefix, it will be functional, but you will lose any // previous data and the old values will not be removed from storage. m = UnorderedMap::new(b"m"); assert!(m.is_empty()); assert_eq!(m.get(&1), Some(&"test".to_string())); // Bug 2: Should not use the same prefix as another collection // or there will be unexpected side effects. let m2 = UnorderedMap::::new(b"m"); assert!(m2.is_empty()); assert_eq!(m2.get(&1), Some(&"test".to_string())); // Bug 3: forgetting to save the collection in storage. When the collection is attached to // the contract state (`self` in `#[near]`) this will be done automatically, but if // interacting with storage manually or working with nested collections, this is relevant. use near_sdk::store::Vector; // Simulate roughly what happens during a function call that initializes state. { let v = Vector::::new(b"v"); near_sdk::env::state_write(&v); } // Simulate what happens during a function call that just modifies the collection // but does not store the collection itself. { let mut v: Vector = near_sdk::env::state_read().unwrap(); v.push(1); // The bug is here that the collection itself if not written back } let v: Vector = near_sdk::env::state_read().unwrap(); // This will report as if the collection is empty, even though the element exists assert!(v.get(0).is_none()); assert!( near_sdk::env::storage_read(&[b"v".as_slice(), &0u32.to_le_bytes()].concat()).is_some() ); // Bug 4 (only relevant for `near_sdk::store`): These collections will cache writes as well // as reads, and the writes are performed on [`Drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html) // so if the collection is kept in static memory or something like `std::mem::forget` is used, // the changes will not be persisted. use near_sdk::store::IterableSet; let mut m: IterableSet = IterableSet::new(b"l"); m.insert(1); assert!(m.contains(&1)); // This would be the fix, manually flushing the intermediate changes to storage. // m.flush(); std::mem::forget(m); m = IterableSet::new(b"l"); assert!(!m.contains(&1)); ``` --- ## Pagination Persistent collections such as `IterableMap/UnorderedMap`, `IterableSet/UnorderedSet` and `Vector` may contain more elements than the amount of gas available to read them all. In order to expose them all through view calls, we can use pagination. With Rust this can be done using iterators with [`Skip`](https://doc.rust-lang.org/std/iter/struct.Skip.html) and [`Take`](https://doc.rust-lang.org/std/iter/struct.Take.html). This will only load elements from storage within the range. ```rust #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { pub status_updates: IterableMap, } #[near] impl Contract { /// Retrieves multiple elements from the `IterableMap`. /// - `from_index` is the index to start from. /// - `limit` is the maximum number of elements to return. pub fn get_updates(&self, from_index: usize, limit: usize) -> Vec<(AccountId, String)> { self.status_updates .iter() .skip(from_index) .take(limit) .collect() } } ``` With JavaScript this can be done using iterators with [`toArray`](https://developer.mozilla.org/en-US/assets/docs/Web/JavaScript/Reference/Global_Objects/Iterator/toArray) and [`slice`](https://developer.mozilla.org/en-US/assets/docs/Web/JavaScript/Reference/Global_Objects/Array/slice). ```ts /// Returns multiple elements from the `UnorderedMap`. /// - `from_index` is the index to start from. /// - `limit` is the maximum number of elements to return. @view({}) get_updates({ from_index, limit }: { from_index: number, limit:number }) { return this.status_updates.toArray().slice(from_index, limit); } ``` ```python # With Python this can be done using standard list slicing. @view def get_updates(self, from_index: int = 0, limit: int = 50) -> list: """Returns multiple elements from the collection with pagination. Args: from_index: The index to start from limit: The maximum number of elements to return Returns: A list of elements from the collection """ # Get all values from the collection all_items = self.status_updates.values() # Apply pagination with list slicing start = min(from_index, len(all_items)) end = min(start + limit, len(all_items)) return all_items[start:end] ``` --- # Source: https://docs.near.org/ai/shade-agents/getting-started/quickstart/components.md --- id: components title: Key Components sidebar_label: Key Components description: "Learn about the components of a simple Shade Agent." --- In this section, we'll explore the main components of the [quickstart template](https://github.com/NearDeFi/shade-agent-template) to understand how to develop a Shade Agent. We'll also look at how to modify the template to build an agent for your use case. --- ## Template Structure The template we're using is a simple Shade Agent built with Hono and written in TypeScript that acts as a verifiable ETH price oracle. It takes prices from two different APIs, takes the average, and then pushes the price to an Ethereum contract. The template also comes with a frontend to make it easier to interact with the Shade Agent. The project has three different APIs: 1) [**agentAccount**](https://github.com/NearDeFi/shade-agent-template/blob/main/src/routes/agentAccount.ts) - This API simply fetches the agent's NEAR account ID and its balance by using the `agentAccountId` and `agent("getBalance")` functions from the `shade-agent-js` library. 2) [**ethAccount**](https://github.com/NearDeFi/shade-agent-template/blob/main/src/routes/ethAccount.ts) - This API returns the `Ethereum Sepolia account` that the Shade Agent uses to update the price of Ethereum in the Sepolia contract. This API is used so the user knows which account to fund for gas. 3) [**transaction**](https://github.com/NearDeFi/shade-agent-template/blob/main/src/routes/transaction.ts) - This is where the core logic of the agent is defined. When this API is called, the agent will build and sign a transaction. We'll look deeper into this API route in the next part. --- ## Signing Transactions In the [transaction API Route](https://github.com/NearDeFi/shade-agent-template/blob/main/src/routes/transaction.ts), the `requestSignature` function from the `shade-agent-js` library is used to sign a transaction. In this example, we're signing a transaction to call an Ethereum contract to update the stored price of ETH. First, we retrieve the price of ETH (in this example, the function queries two different APIs and calculates the average). ``` const ethPrice = await getEthereumPriceUSD(); if (!ethPrice) { ``` Next, we build the `transaction payload` to be signed. To do this, we're using the `chainsig.js` library. Using this library, we: 1. `Derive the Ethereum address` that will be sending the transaction. This function takes the agent contract account ID since this is the predecessor account that is calling the Chain Signatures [MPC contract](https://github.com/Near-One/mpc/tree/main/libs/chain-signatures/contract), and a path. The path can be whatever string you like, different paths will derive different addresses. 2. Create the `data`. This is what action we're performing, in this case, a function call to update the price in the contract. 3. `Build the transaction and the transaction payload` by inputting the derived address, the target Ethereum smart contract, and the data. ``` // Derive the price pusher Ethereum address const { address: senderAddress } = await Evm.deriveAddressAndPublicKey( contractId, "ethereum-1", ); // Create a new JSON-RPC provider for the Ethereum network const provider = new JsonRpcProvider(ethRpcUrl); // Create a new contract interface for the Ethereum Oracle contract const contract = new Contract(ethContractAddress, ethContractAbi, provider); // Encode the function data for the updatePrice function const data = contract.interface.encodeFunctionData("updatePrice", [ethPrice]); // Prepare the transaction for signing const { transaction, hashesToSign } = await Evm.prepareTransactionForSigning({ from: senderAddress, to: ethContractAddress, data, }); ``` Once we have the payload (also known as the hash), we can call the `requestSignature` function to sign the transaction. We specify the `keyType` as `Ecdsa` as we're signing for a blockchain that uses the `secp256k1` signature scheme. ``` const signRes = await requestSignature({ path: "ethereum-1", payload: uint8ArrayToHex(hashesToSign[0]), keyType: "Ecdsa", }); console.log("signRes", signRes); ``` The result is the `signature`. We then attach the signature to the Ethereum transaction and broadcast it to the target network. ``` // Reconstruct the signed transaction const signedTransaction = Evm.finalizeTransactionSigning({ transaction, rsvSignatures: [toRSV(signRes)], }); // Broadcast the signed transaction const txHash = await Evm.broadcastTx(signedTransaction); ``` --- ## Using Different Chains We set up a chain adapter for Ethereum Sepolia in the [Ethereum.ts](https://github.com/NearDeFi/shade-agent-template/blob/main/src/utils/ethereum.ts) file using the `chainsig.js` library. This library allows us to easily construct transaction payloads to be signed by the agent. ``` // Set up a public client for the Ethereum network const publicClient = createPublicClient({ transport: http(ethRpcUrl), }); // Set up a chain signatures chain adapter for the Ethereum network export const Evm = new chainAdapters.evm.EVM({ publicClient, contract: MPC_CONTRACT, }) as any; ``` You can set up chain adapters for a variety of chains, including EVM, Bitcoin, NEAR, Solana, SUI, XRP, Cosmos, and more to allow your agent to interact with multiple different chains. You can see a full list of the chains currently supported [here](https://github.com/NearDeFi/chainsig.js/tree/main?tab=readme-ov-file#supported-chains), but feel free to contribute any chain that is not yet supported. Implementation details differ slightly from chain to chain; as such, we recommend you review our [chain signature docs](../../../../chain-abstraction/chain-signatures/implementation.md). Note that step 3 of requesting a signature is different; we use the `requestSignature` function from `shade-agent-js` instead. If you are using a chain that uses the `ed25519` signature scheme (NEAR, Solana, SUI, Aptos, etc.), you should specify the `keyType` as `Eddsa` when calling `requestSignature`. --- ## Next Steps Now that you've explored the basics of Shade Agents, we recommend diving deeper into the [framework overview](../../concepts/framework-overview.md) to understand the core concepts needed for building production-ready Shade Agents. --- # Source: https://docs.near.org/smart-contracts/contracts-list.md --- id: contracts-list title: Contracts List description: "Explore NEAR contracts deployed across projects." --- List of contracts of various projects deployed on NEAR. This list is not exhaustive and is meant to be a reference for developers and users :::info Find more: [NEAR Catalog](https://nearcatalog.xyz) - A catalog of NEAR projects [NEAR Audit Database](https://github.com/NEARBuilders/audits) - List of Public Audits in the NEAR Ecosystem ::: ## Contracts List | Contract | Project | Tags | Description | |-------------------------------------------------------------------------------------------------|----------------------------------------------------|----------------------------------|-------------------------------------------------------------------------------------------------------| | [Burrow.finance (public archive)](https://github.com/NearDefi/burrowland) | **[Rhea finance](https://rhea.finance/)** | DeFi | Lending platform | | [ref-exchange](https://github.com/ref-finance/ref-contracts/tree/main/ref-exchange) | **[Rhea finance](https://rhea.finance/)** | DeFi | DEX Contract | | [ref-farming](https://github.com/ref-finance/ref-contracts/tree/main/ref-farming) | **[Rhea finance](https://rhea.finance/)** | DeFi | DEX LP Farming | | [Meta Token](https://github.com/Narwallets/meta-pool/tree/master/meta-token) | **[Metapool](https://www.metapool.app/)** | FT | FT contract | | [metapool](https://github.com/Narwallets/meta-pool/tree/master/metapool) | **[Metapool](https://www.metapool.app/)** | staking | Liquid staking | | [donation](https://github.com/PotLock/core/blob/main/contracts/donation) | **[PotLock](https://www.potlock.org/)** | FT, donation | Donating FT to any contract | | [pot](https://github.com/PotLock/core/blob/main/contracts/pot) | **[PotLock](https://www.potlock.org/)** | donation | QF Funding round | | [pot_factory](https://github.com/PotLock/core/blob/main/contracts/pot_factory) | **[PotLock](https://www.potlock.org/)** | factory, donation | Factory contract for QF Funding Rounds | | [registry](https://github.com/PotLock/core/blob/main/contracts/registry) | **[PotLock](https://www.potlock.org/)** | donation, misc | Registry for QF funding rounds | | [sybil](https://github.com/PotLock/core/blob/main/contracts/sybil) | **[PotLock](https://www.potlock.org/)** | misc | Registry for sybil resistance providers | | [ShardDog-NFT-Protocol](https://github.com/joe-rlo/ShardDog-NFT-Protocol) | **[ShardDog](https://shard.dog/)** | NFT | NFT contract managing multiple NFT collections | | [NFT-Minting-withFT](https://github.com/joe-rlo/NFT-Minting-withFT) | **[ShardDog](https://shard.dog/)** | NFT, FT | NFT contract supports minting with FT tokens, royalties, payment distribution, premint functionality. | | [contest-contract](https://github.com/joe-rlo/contest-contract) | **[ShardDog](https://shard.dog/)** | raffle | Weighted raffle system. | | [core](https://github.com/near/intents/tree/main/core) | **[NEAR Intents](https://near-intents.org/)** | cross-chain, DeFi | Protocol for multichain financial products | | [contest-contract](https://github.com/NEAR-DevHub/race-of-sloths/tree/main/contest-contract) | **[Race of Sloths](https://race-of-sloths.com/)** | raffle | Weighted raffle contract | | [contract](https://github.com/NEAR-DevHub/race-of-sloths/tree/main/contract) | **[Race of Sloths](https://race-of-sloths.com/)** | misc | Scoreboard including streaks | | [contract-mvp](https://github.com/Templar-Protocol/contract-mvp) | **[Templar Protocol](https://www.templarfi.org/)** | DeFi | Permissionless cypher lending | | [index-fund-contract](https://github.com/Templar-Protocol/index-fund-contract) | **[Templar Protocol](https://www.templarfi.org/)** | DeFi | Index fund contract | | [linear](https://github.com/linear-protocol/LiNEAR/tree/main/contracts/linear) | **[Linear](https://linearprotocol.org/)** | staking | Liquid staking | | [hodl-lockup](https://github.com/sweatco/hodl-lockup) | **[Sweat Economy](https://sweateconomy.com/)** | FT, lockup | FT lockup contract | | [sweat-jar](https://github.com/sweatco/sweat-jar) | **[Sweat Economy](https://sweateconomy.com/)** | FT, staking | FT staking contract | | [sweat-claim](https://github.com/sweatco/sweat-claim/tree/main/contract) | **[Sweat Economy](https://sweateconomy.com/)** | FT | FT managing contract | | [sweat-booster](https://github.com/sweatco/sweat-booster) | **[Sweat Economy](https://sweateconomy.com/)** | FT, NFT | NFT vouchers for FT | | [multisig](https://github.com/sweatco/multisig) | **[Sweat Economy](https://sweateconomy.com/)** | multisig | Multisig Contract | | [sweat-burn](https://github.com/sweatco/sweat-burn) | **[Sweat Economy](https://sweateconomy.com/)** | FT | FT burning | | [mb-factory-v2](https://github.com/Mintbase/mb-contracts/tree/main/mb-factory-v2) | **MintBase** | factory, NFT | NFT factory contract | | [mb-interop-market](https://github.com/Mintbase/mb-contracts/tree/main/mb-interop-market) | **MintBase** | NFT | NFT Market contract | | [mb-nft-v2](https://github.com/Mintbase/mb-contracts/tree/main/mb-nft-v2) | **MintBase** | NFT | NFT contract | | [neardevhub-contract](https://github.com/NEAR-DevHub/neardevhub-contract) | **[NEAR DevHub](https://neardevhub.org/)** | misc | Community managing platform | | [lockup-factory](https://github.com/near/core-contracts/tree/master/lockup-factory) | **Core-Contracts** | factory, lockup | Factory contract deploying lockup contracts | | [lockup](https://github.com/near/core-contracts/tree/master/lockup) | **Core-Contracts** | lockup | Locks and holds an owner's tokens for a lockup period | | [multisig-factory](https://github.com/near/core-contracts/tree/master/multisig-factory) | **Core-Contracts** | factory | Factory contract for multisig | | [multisig](https://github.com/near/core-contracts/tree/master/multisig) | **Core-Contracts** | multisig | multisig | | [multisig2](https://github.com/near/core-contracts/tree/master/multisig2) | **Core-Contracts** | multisig | multisig | | [staking-pool-factory](https://github.com/near/core-contracts/tree/master/staking-pool-factory) | **Core-Contracts** | factory, staking-pool, validator | Staking Pool factory contract | | [staking-pool](https://github.com/near/core-contracts/tree/master/staking-pool) | **Core-Contracts** | staking-pool, validator | Staking pool contract | | [state-manipulation](https://github.com/near/core-contracts/tree/master/state-manipulation) | **Core-Contracts** | state-manipulation | Contracts state-manipulation | | [voting](https://github.com/near/core-contracts/tree/master/voting) | **Core-Contracts** | validator | Validator Voting contract | | [w-near](https://github.com/near/core-contracts/tree/master/w-near) | **Core-Contracts** | FT | FT contract that wraps native NEAR | | [whitelist](https://github.com/near/core-contracts/tree/master/whitelist) | **Core-Contracts** | whitelist, staking-pool | Whitelist contract for staking pools | --- # Source: https://docs.near.org/api/rpc/contracts.md --- id: contracts title: Accounts / Contracts description: "Learn to query information from accounts and contracts using the RPC" hide_table_of_contents: true --- The RPC API enables you to view details about accounts and contracts as well as perform contract calls. ## Quick Reference | Method | Endpoint | Purpose | |--------|----------|---------| | [`view_account`](#view-account) | Query account info | Get basic account information | | [`view_account_changes`](#view-account-changes) | Track account changes | Monitor account state changes | | [`view_code`](#view-contract-code) | Query contract code | Get deployed contract WASM code | | [`view_state`](#view-contract-state) | Query contract state | Get contract storage data | | [`data_changes`](#view-contract-state-changes) | Track state changes | Monitor contract state changes | | [`contract_code_changes`](#view-contract-code-changes) | Track code changes | Monitor contract deployments | | [`call_function`](#call-a-contract-function) | Call view functions | Execute read-only contract methods | --- ## View account {#view-account} Returns basic account information. - **method**: `query` - **params**: - `request_type`: `view_account` - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) - `account_id`: _`"example.testnet"`_ ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "query", "params": { "request_type": "view_account", "finality": "final", "account_id": "account.rpc-examples.testnet" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.query({ request_type: 'view_account', finality: 'final', account_id: 'account.rpc-examples.testnet', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=query \ params:='{ "request_type": "view_account", "finality": "final", "account_id": "account.rpc-examples.testnet" }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "amount": "999788200694421800000000", "block_hash": "56xEo2LorUFVNbkFhCncFSWNiobdp1kzm14nZ47b5JVW", "block_height": 187440904, "code_hash": "11111111111111111111111111111111", "locked": "0", "storage_paid_at": 0, "storage_usage": 410 }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## View account changes {#view-account-changes} Returns account changes from transactions in a given account. - **method**: `changes` - **params**: - `changes_type`: `account_changes` - `account_ids`: [`"example.testnet"`] - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "changes", "params": { "changes_type": "account_changes", "account_ids": ["contract.rpc-examples.testnet"], "block_id": 187310139 } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.accountChanges(['contract.rpc-examples.testnet'], { blockId: 187310139, }); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=changes \ params:='{ "changes_type": "account_changes", "account_ids": ["contract.rpc-examples.testnet"], "block_id": 187310139 }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "block_hash": "8woqfx6kyjgfgU1S2L6Kur27h5jpBtDTmG8vQ8vpAUut", "changes": [ { "cause": { "receipt_hash": "FseKd4rmjPSAuEz9zh9b5PfUS4jJV4rB6XkeHwVkyXkk", "type": "receipt_processing" }, "change": { "account_id": "contract.rpc-examples.testnet", "amount": "4999184472524996100000000", "code_hash": "GVvBFWDPNmomMwXH4LvQW2cRaZJ8N6gxsdBhbJ8ReVJf", "locked": "0", "storage_paid_at": 0, "storage_usage": 81621 }, "type": "account_update" }, { "cause": { "receipt_hash": "FseKd4rmjPSAuEz9zh9b5PfUS4jJV4rB6XkeHwVkyXkk", "type": "action_receipt_gas_reward" }, "change": { "account_id": "contract.rpc-examples.testnet", "amount": "4999212038891301300000000", "code_hash": "GVvBFWDPNmomMwXH4LvQW2cRaZJ8N6gxsdBhbJ8ReVJf", "locked": "0", "storage_paid_at": 0, "storage_usage": 81621 }, "type": "account_update" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## View contract code {#view-contract-code} Returns the contract code (Wasm binary) deployed to the account. Please note that the returned code will be encoded in base64. - **method**: `query` - **params**: - `request_type`: `view_code` - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) - `account_id`: `"example.testnet"`, ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "query", "params": { "request_type": "view_code", "finality": "final", "account_id": "contract.rpc-examples.testnet" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.query({ request_type: 'view_code', finality: 'final', account_id: 'contract.rpc-examples.testnet', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=query \ params:='{ "request_type": "view_code", "finality": "final", "account_id": "contract.rpc-examples.testnet" }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "block_hash": "bxucHpnP8VsiB3pLvA7DpBwri9x1DCZxVfBNkrdWbqn", "block_height": 187441984, "code_base64": "AGFzbQEAAAABugEbYAJ/fwF/YAN/f38Bf2ACf38AYAN/...", "hash": "GVvBFWDPNmomMwXH4LvQW2cRaZJ8N6gxsdBhbJ8ReVJf" }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## View contract state {#view-contract-state} Returns the state (key value pairs) of a contract based on the key prefix (base64 encoded). Pass an empty string for `prefix_base64` if you would like to return the entire state. Please note that the returned state will be base64 encoded as well. - **method**: `query` - **params**: - `request_type`: `view_state` - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) - `account_id`: `"example.testnet"`, - `prefix_base64`: `""` ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "query", "params": { "request_type": "view_state", "finality": "final", "account_id": "contract.rpc-examples.testnet", "prefix_base64": "" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.query({ request_type: 'view_state', finality: 'final', account_id: 'contract.rpc-examples.testnet', prefix_base64: '', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=query \ params:='{ "request_type": "view_state", "finality": "final", "account_id": "contract.rpc-examples.testnet", "prefix_base64": "" }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "block_hash": "GN5R7S8mMTEkUT1njWu9jARV29G7izVDjdSNs976BJVw", "block_height": 187442491, "values": [ { "key": "U1RBVEU=", "value": "HQAAAEdyZWV0aW5ncyBmcm9tIE5FQVIgUHJvdG9jb2whAQAAAHI=" }, { "key": "cgEAAAAAAAAA", "value": "FQAAAEhlbGxvLCBOZWFyIFByb3RvY29sIQ==" } ] }, "id": "dontcare" } ``` **Note**: Currently, the response includes a `proof` field directly in the `result`, and a `proof` fields on each element of the `values` list. In the future, the `result.proof` will be included only if the result is **not empty**, and the `proof` field will be removed from all `values`. When parsing the result, you should accept objects with or without these fields set.
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## View contract state changes {#view-contract-state-changes} Returns the state change details of a contract based on the key prefix (encoded to base64). Pass an empty string for this param if you would like to return all state changes. - **method**: `changes` - **params**: - `changes_type`: `data_changes` - `account_ids`: `["example.testnet"]`, - `key_prefix_base64`: `"base64 encoded key value"`, - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "changes", "params": { "changes_type": "data_changes", "account_ids": ["contract.rpc-examples.testnet"], "key_prefix_base64": "", "block_id": 187310139 } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.contractStateChanges( ['contract.rpc-examples.testnet'], { blockId: 187310139 }, '' ); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=changes \ params:='{ "changes_type": "data_changes", "account_ids": ["contract.rpc-examples.testnet"], "key_prefix_base64": "", "block_id": 187310139 }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "block_hash": "8woqfx6kyjgfgU1S2L6Kur27h5jpBtDTmG8vQ8vpAUut", "changes": [ { "cause": { "receipt_hash": "FseKd4rmjPSAuEz9zh9b5PfUS4jJV4rB6XkeHwVkyXkk", "type": "receipt_processing" }, "change": { "account_id": "contract.rpc-examples.testnet", "key_base64": "U1RBVEU=", "value_base64": "HQAAAEdyZWV0aW5ncyBmcm9tIE5FQVIgUHJvdG9jb2whAQAAAHI=" }, "type": "data_update" }, { "cause": { "receipt_hash": "FseKd4rmjPSAuEz9zh9b5PfUS4jJV4rB6XkeHwVkyXkk", "type": "receipt_processing" }, "change": { "account_id": "contract.rpc-examples.testnet", "key_base64": "cgEAAAAAAAAA", "value_base64": "FQAAAEhlbGxvLCBOZWFyIFByb3RvY29sIQ==" }, "type": "data_update" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## View contract code changes {#view-contract-code-changes} Returns code changes made when deploying a contract. Change is returned is a base64 encoded WASM file. - **method**: `changes` - **params**: - `changes_type`: `contract_code_changes` - `account_ids`: `["example.testnet"]`, - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "changes", "params": { "changes_type": "contract_code_changes", "account_ids": ["contract.rpc-examples.testnet"], "block_id": 187309439 } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://archival-rpc.testnet.near.org", }); const response = await provider.contractCodeChanges( ['contract.rpc-examples.testnet'], { blockId: 187309439 } ); ``` ```bash http POST https://archival-rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=changes \ params:='{ "changes_type": "contract_code_changes", "account_ids": ["contract.rpc-examples.testnet"], "block_id": 187309439 }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "block_hash": "D1ZY3y51Z2v5tXq2nZPmXHgA3zZsPBzbtwHXjCvAEuLV", "changes": [ { "cause": { "receipt_hash": "AR4cxtxc52WfnZcGEZHmPfQ1Dk3vQNb7vjSyicykfJWZ", "type": "receipt_processing" }, "change": { "account_id": "contract.rpc-examples.testnet", "code_base64": "AGFzbQEAAAABugEbYAJ/fwF/YAN/f38Bf2ACf38AYAN/..." }, "type": "contract_code_update" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Call a contract function {#call-a-contract-function} Allows you to call a contract method as a [view function](../../tools/near-cli#contract). - **method**: `query` - **params**: - `request_type`: `call_function` - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) - `account_id`: _`"example.testnet"`_ - `method_name`: `get_method_name` (example [`view` methods](https://github.com/near/core-contracts/blob/master/staking-pool/src/lib.rs#L317)) - `args_base64`: `method_arguments_base_64_encoded` ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "query", "params": { "request_type": "call_function", "finality": "final", "account_id": "contract.rpc-examples.testnet", "method_name": "get_greeting", "args_base64": "" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.query({ request_type: 'call_function', finality: 'final', account_id: 'contract.rpc-examples.testnet', method_name: 'get_greeting', args_base64: '', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=query \ params:='{ "request_type": "call_function", "finality": "final", "account_id": "contract.rpc-examples.testnet", "method_name": "get_greeting", "args_base64": "" }' ``` }>
Example response:

```json { "jsonrpc": "2.0", "result": { "block_hash": "GTZdXfNmnL6TkJFdBeVMHCadgLuKChVfRNCSVsEQoJ7L", "block_height": 187444191, "logs": [], "result": [ 34, 71, 114, 101, 101, 116, 105, 110, 103, 115, 32, 102, 114, 111, 109, 32, 78, 69, 65, 82, 32, 80, 114, 111, 116, 111, 99, 111, 108, 33, 34 ] }, "id": "dontcare" } ``` **Note**: `[34, 71, ..., 33, 34]` is an array of bytes, to be specific it is an ASCII code of `"Greetings from NEAR Protocol!"`. `near-sdk-rs` and `near-sdk-js` return JSON-serialized results.

Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- The `args_base64` in this example can be decoded as ```json { "record_id": 1 } ``` ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "query", "params": { "request_type": "call_function", "finality": "final", "account_id": "contract.rpc-examples.testnet", "method_name": "get_record", "args_base64": "ewogICJyZWNvcmRfaWQiOiAxCn0=" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.query({ request_type: 'call_function', finality: 'final', account_id: 'contract.rpc-examples.testnet', method_name: 'get_record', args_base64: 'ewogICJyZWNvcmRfaWQiOiAxCn0=', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=query \ params:='{ "request_type": "call_function", "finality": "final", "account_id": "contract.rpc-examples.testnet", "method_name": "get_record", "args_base64": "ewogICJyZWNvcmRfaWQiOiAxCn0=" }' ``` }>
Example response:

```json { "jsonrpc": "2.0", "result": { "block_hash": "8Gp8x1ZcanL3C2ris9rgk1nY8v6MuickLWeM6Gj2jGKs", "block_height": 187445443, "logs": [], "result": [ 34, 72, 101, 108, 108, 111, 44, 32, 78, 101, 97, 114, 32, 80, 114, 111, 116, 111, 99, 111, 108, 33, 34 ] }, "id": "dontcare" } ``` **Note**: `[34, 72, ..., 108, 33, 34]` is an array of bytes, to be specific it is an ASCII code of `"Hello, Near Protocol!"`. `near-sdk-rs` and `near-sdk-js` return JSON-serialized results.

Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Error Handling ### Common Error Types | Error Code | Description | Solution | |------------|-------------|----------| | `UnknownAccount` | Account does not exist | Check account ID spelling and existence | | `InvalidAccount` | Invalid account format | Use valid account ID format (e.g., `account.near`) | | `UnknownBlock` | Block not found | Use a valid block hash or height | | `GarbageCollectedBlock` | Block too old | Use archival node or more recent block | | `TooManyInputs` | Too many accounts in request | Reduce number of accounts per request | | `NoContractCode` | Account has no contract deployed | Verify the account has a deployed contract | | `MethodNotFound` | Contract method does not exist | Check method name and contract ABI | | `InvalidArgs` | Invalid method arguments | Verify args format and encoding | --- ## Best Practices - **Use specific queries**: Query only the data you need instead of broad state queries - **Validate inputs**: Always validate method arguments before contract calls --- # Source: https://docs.near.org/tutorials/examples/count-near.md --- id: count-near title: Count on NEAR description: "A simple counter on NEAR Protocol." --- 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 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 ├── 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 ``` --- ## 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. ``` import '@/styles/globals.css'; import '@near-wallet-selector/modal-ui/styles.css'; import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet'; import { setupMeteorWalletApp } from '@near-wallet-selector/meteor-wallet-app'; import { setupBitteWallet } from '@near-wallet-selector/bitte-wallet'; import { setupEthereumWallets } from '@near-wallet-selector/ethereum-wallets'; import { setupHotWallet } from '@near-wallet-selector/hot-wallet'; import { setupLedger } from '@near-wallet-selector/ledger'; import { setupSender } from '@near-wallet-selector/sender'; import { setupHereWallet } from '@near-wallet-selector/here-wallet'; import { setupNearMobileWallet } from '@near-wallet-selector/near-mobile-wallet'; import { setupWelldoneWallet } from '@near-wallet-selector/welldone-wallet'; import { WalletSelectorProvider } from '@near-wallet-selector/react-hook'; import { wagmiAdapter, web3Modal } from '@/wallets/web3modal'; import { Navigation } from '@/components/Navigation'; import { NetworkId, CounterContract } from '@/config'; const walletSelectorConfig = { network: NetworkId, createAccessKeyFor: CounterContract, modules: [ setupEthereumWallets({ wagmiConfig: wagmiAdapter.wagmiConfig, web3Modal }), setupBitteWallet(), setupMeteorWallet(), setupMeteorWalletApp({contractId: CounterContract}), setupHotWallet(), setupLedger(), setupSender(), setupHereWallet(), setupNearMobileWallet(), setupWelldoneWallet(), setupMyNearWallet(), ], } export default function App({ Component, pageProps }) { return ( ); } ``` ``` import { useEffect, useState } from 'react'; import styles from '@/styles/app.module.css'; import { useWalletSelector } from '@near-wallet-selector/react-hook'; import { CounterContract } from '@/config'; export default function Home() { const { signedAccountId, callFunction, viewFunction } = useWalletSelector(); const [number, setNumber] = useState(0); const [numberIncrement, setNumberIncrement] = useState(0); const [leftEyeVisible, setLeftEyeVisible] = useState(true); const [rightEyeVisible, setRightEyeVisible] = useState(true); const [tongueVisible, setTongueVisible] = useState(false); const [dotOn, setDotOn] = useState(true); const [globalInterval, setGlobalInterval] = useState(null); useEffect(() => { fetchNumber(); // Fetch the number every two seconds let interval = setInterval(fetchNumber, 1500); setGlobalInterval(interval); return () => clearInterval(interval); }, []) useEffect(() => { // interrupt the constant fetching of the number clearInterval(globalInterval); // Debounce the increment call until the user stops clicking const getData = setTimeout(() => { if (numberIncrement === 0) return; setNumberIncrement(0); // Try to increment the counter, fetch the number afterwords callFunction({ contractId: CounterContract, method: 'increment', args: { number: numberIncrement } }) .finally(() => { fetchNumber(); let interval = setInterval(fetchNumber, 1500) setGlobalInterval(interval); }) }, 500) return () => clearTimeout(getData); }, [numberIncrement]) const fetchNumber = async () => { setDotOn(true); console.log("fetching number") const num = await viewFunction({ contractId: CounterContract, method: "get_num" }); setNumber(num); setDotOn(false); } const call = (method) => async () => { const methodToState = { increment: () => { setNumberIncrement(numberIncrement + 1) setNumber(number + 1) }, decrement: () => { setNumberIncrement(numberIncrement - 1) setNumber(number - 1) }, reset: async () => { setNumberIncrement(0) setNumber(0) callFunction({ contractId: CounterContract, method: 'reset' }).then(async () => { await fetchNumber(); }) }, } methodToState[method]?.(); } return (

This global counter lives in the NEAR blockchain!

{!signedAccountId &&

You'll need to sign in to interact with the counter:

}
= 0 ? 'smile' : 'cry'}`}>
{number}
); } ```
--- ## 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. ``` @NearBindgen({}) class Counter { val: number = 0; static schema = { number: 'number' } @view({}) // Public read-only method: Returns the counter value. get_num(): number { return this.val } @call({}) // Public method: Increment the counter. increment({ number = 1 }: { number: number }) { this.val += number; near.log(`Increased number to ${this.val}`) } @call({}) // Public method: Decrement the counter. decrement({ number = 1 }: { number: number }) { this.val -= number; near.log(`Decreased number to ${this.val}`) } @call({}) // Public method - Reset to zero. reset() { this.val = 0; near.log(`Reset counter to zero`) } } ``` ``` #[near(contract_state)] #[derive(Default)] pub struct Counter { val: i8, } // Implement the contract structure #[near] impl Counter { // Public read-only method: Returns the counter value. pub fn get_num(&self) -> i8 { return self.val; } // Public method: Increment the counter. pub fn increment(&mut self, number: Option) { self.val += number.unwrap_or(1); log!("Increased number to {}", self.val); } // Public method: Decrement the counter. pub fn decrement(&mut self, number: Option) { self.val -= number.unwrap_or(1); log!("Decreased number to {}", self.val); } // Public method - Reset to zero. pub fn reset(&mut self) { self.val = 0; log!("Reset counter to zero"); } } /* ``` --- ### 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/counter.wasm ``` ```bash cargo near deploy build-non-reproducible-wasm ``` :::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` ::: --- # Source: https://docs.near.org/tutorials/protocol/create-account.md --- id: create-account title: Create a NEAR Account description: "Understand how to create a NEAR account using a wallet and the NEAR CLI" --- To start developing applications in NEAR you will need a NEAR `testnet` account. This account will allow you to deploy and test your applications without spending any real money. You can create a `testnet` NEAR account through one of the following methods: - [Using one of the wallets listed in wallet.near.org](#using-a-wallet) - [Using the command line interface (CLI)](#through-the-cli) :::tip If you already have a NEAR account and want to use it in another wallet or the CLI, check out our guide on [Importing a NEAR Account](importing-account) ::: --- ## Using a Wallet Go to [wallet.near.org](https://wallet.near.org/) and choose one of the wallets listed there. Do not worry, the list has been curated, so you can be sure that all wallets listed there are safe to use. In general, all wallets will offer similar functionality, so in theory you can choose any of them. However, know that some wallets will readily allow you to create [named accounts](../../protocol/account-id.md#named-address) (e.g. `alice.testnet`), which are easier to remember. Remember to write down the seed phrase, as it is the only way to access your account! :::warning Testnet Make sure to create a `testnet` account (ending with `.testnet`, e.g. `alice.testnet`), and not a `mainnet` account (ending with `.near`). NEAR `testnet` is a separate network that allows you to test your applications without spending real money. ::: :::tip Funding the Wallet Need `testnet` funds? try using one of our [faucets](../../faucet.md) ::: --- ## Through the CLI When developing smart contracts you will expend lots of time interacting with the NEAR blockchain through the command line interface (CLI). First, you will need to install the [NEAR CLI](../../tools/cli.md#installation): ```bash npm install -g near-cli-rs@latest ``` ``` $ cargo install near-cli-rs ``` ```bash curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh ``` ```bash irm https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.ps1 | iex ``` Once you have the CLI installed, you can create a new account using the following command: ```bash near create-account --useFaucet ``` This command will create a new account with the name `` and fund it using the `testnet` faucet. Make sure to replace `` with your desired account name. --- ## Next Steps Now that you have created a NEAR account, you can start developing applications on the NEAR Protocol. Here are some resources to help you get started: - Fund the wallet through one of our [faucets](../../faucet.md) - Create your [first smart contract](../../smart-contracts/quickstart.md) - Build a [Web3 Application](../../web3-apps/quickstart.md) - Learn how to build an [Auction App from end-to-end](../../tutorials/auction/0-intro.md) --- # Source: https://docs.near.org/integrations/create-transactions.md --- id: create-transactions title: Create Transactions sidebar_label: Create a Transaction description: "Learn how to create, sign, and send transactions on NEAR protocol, including transaction structure, actions, and best practices for integration." --- To construct & process transactions you will need our API JavaScript library: [`near-api-js`](../tools/near-api.md). There are many ways to create transactions but for this example we'll show you two ways to create a simple token transfer transaction. - [HIGH LEVEL](#high-level----create-a-transaction) - _easiest way to create a transaction_ - [LOW LEVEL](#low-level----create-a-transaction) - _performs the exact same transaction as above, but deconstructs the entire process for those curious about each step_ At the core, all transactions require the following: - `signerId` _(account ID of the transaction originator)_ - `signerPublicKey` - `receiverId` _(account ID of the transaction recipient)_ - `nonceForPublicKey` _(each time a key is used the nonce value should be incremented by 1)_ - `actions` _( [[click here]](/protocol/transaction-anatomy#actions) for supported arguments)_ - `blockHash` _(a current block hash (within 24hrs) to prove the transaction was recently created)_ See [Transaction Class](https://near.github.io/near-api-js/classes/near-api-js.transaction.Transaction.html) for a more in depth outline. --- ## HIGH LEVEL -- Create a transaction ### Setup 1. Clone the [transaction-examples](https://github.com/near-examples/transaction-examples) repository by running: ```bash git clone https://github.com/near-examples/transaction-examples.git ``` 2. Follow [setup instructions](https://github.com/near-examples/transaction-examples/blob/master/README.md#prerequisites) ### Imports In [`send-tokens-easy.js`](https://github.com/near-examples/transaction-examples/blob/9e999253aafa2c3e3b537810a0b8ce7596c3506c/send-tokens-easy.js#L1-L5) we use two dependencies: 1. [NEAR API JavaScript library](https://github.com/near/near-api-js) 2. [`dotenv`](https://www.npmjs.com/package/dotenv) (used to load environment variables for private key) ```js const { Near, Account, KeyPair, keyStores, utils } = require("near-api-js"); require("dotenv").config(); ``` The destructured utilities from near-api-js that you will use to interact with the blockchain: - `Near` - create a connection to NEAR passing configuration variables - `Account` - creates an account object for interacting with the blockchain - `KeyPair` - creates a keyPair from the private key you'll provide in an `.env` file - `keyStores` - stores the keyPair that you will create from the private key and used to sign Transactions - `utils` - used to format NEAR amounts ### Accounts & Network Next, you'll need to enter the `accountId` of the `sender` and `receiver`, as well as the `networkId` (`betanet`, `testnet`, or `mainnet`). ```js const sender = "sender.testnet"; const receiver = "receiver.testnet"; const networkId = "testnet"; ``` ### Formatting Token Amounts When sending NEAR tokens (Ⓝ) during a transaction, the amount needs to be converted into [Yocto](https://en.wikipedia.org/wiki/Yocto-) Ⓝ or (10^-24). - To perform this you will use the [`near-api-js`](https://github.com/near/near-api-js) method [`parseNearAmount()`](https://github.com/near/near-api-js/blob/d4d4cf1ac3182fa998b1e004e6782219325a641b/src/utils/format.ts#L53-L63) (located in `utils/format`) ```js const amount = utils.format.parseNearAmount("1.5"); ``` ### Create a Key Store In order to sign transactions you will need to create a "Key Store" that will hold a [full access key](/protocol/access-keys#full-access-keys) to sign your transactions. There are several ways to accomplish this, but for this example we will use a private key stored in either an `.env` file in your project or an environment variable exported globally. - If you created the account using [`near-cli`](../tools/cli.md) or ran [`near login`](../tools/cli.md#import) in your terminal, your private key can be found in the your machine's keychain. - If you created an account using [NEAR Wallet](https://testnet.mynearwallet.com/), your key will be found in your browser's `Local Storage`. - In your browser's dev tools... `Application` >> `Storage` >> `Local Storage` ```js // sets up an empty keyStore object in memory using near-api-js const keyStore = new keyStores.InMemoryKeyStore(); // creates a keyPair from the private key provided in your .env file const keyPair = KeyPair.fromString(process.env.SENDER_PRIVATE_KEY); // adds the key you just created to your keyStore which can hold multiple keys (must be inside an async function) await keyStore.setKey(networkId, sender, keyPair); ``` ### Setting up a connection to NEAR Now create a connection to NEAR using a configuration object that will contain your `networkId` setup earlier as well as your `keyStore`. ```js // configuration used to connect to NEAR const prefix = (networkId === "testnet") ? "testnet" : "www"; const config = { networkId, keyStore, nodeUrl: `https://rpc.${networkId}.near.org`, walletUrl: `https://wallet.${networkId}.near.org`, helperUrl: `https://helper.${networkId}.near.org`, explorerUrl: `https://${prefix}.nearblocks.io`, }; // connect to NEAR! :) const near = new Near(config); // create a NEAR account object const senderAccount = new Account(near.connection, sender); ``` You'll notice the last line uses your NEAR connection to create a `senderAccount` object that you'll use to perform the transaction. ### Create, Sign, & Send Transaction Now that everything is setup, creating the transaction is a single line of code. ```js const result = await senderAccount.sendMoney(receiver, amount); ``` This simple command constructs, signs, and sends a token transfer transaction on the NEAR blockchain. There is no need to create a `result` variable aside from inspecting the response details from your transaction and even create a link to [NearBlocks Explorer](https://testnet.nearblocks.io/) to view a GUI version of the transaction details. --- ## LOW LEVEL -- Create a Transaction ### Setup 1. Clone the [transaction-examples](https://github.com/near-examples/transaction-examples) repository by running: ```bash git clone https://github.com/near-examples/transaction-examples.git ``` 2. Follow [setup instructions](https://github.com/near-examples/transaction-examples/blob/master/README.md#prerequisites) --- ### Imports In [`send-tokens-deconstructed.js`](https://github.com/near-examples/transaction-examples/blob/master/send-tokens-deconstructed.js#L1-L4) we use three dependencies: 1. [NEAR API JavaScript library](https://github.com/near/near-api-js) 2. [`js-sha256`](https://www.npmjs.com/package/js-sha256) (cryptographic hashing algorithm) 3. [`dotenv`](https://www.npmjs.com/package/dotenv) (used to load environment variables) ```js const { providers, KeyPair, utils, transactions } = require("near-api-js"); const sha256 = require("js-sha256"); require("dotenv").config(); ``` --- ### Accounts & Network Next, you'll need to enter the `accountId` of the `sender` and `receiver`, as well as the `networkId` (`betanet`, `testnet`, or `mainnet`). ```js const sender = "sender.testnet"; const receiver = "receiver.testnet"; const networkId = "testnet"; ``` --- ### Formatting Token Amounts When sending NEAR tokens (Ⓝ) during a transaction, the amount needs to be converted into [Yocto](https://en.wikipedia.org/wiki/Yocto-) Ⓝ or (10^-24). - To perform this you will use the [`near-api-js`](https://github.com/near/near-api-js) method [`parseNearAmount()`](https://github.com/near/near-api-js/blob/d4d4cf1ac3182fa998b1e004e6782219325a641b/src/utils/format.ts#L53-L63) (located in `utils/format`) ```js const amount = utils.format.parseNearAmount("1.5"); ``` --- ### Setting up a connection to NEAR In this example, we will create a NEAR RPC `provider` that allows us to interact with the chain via [RPC endpoints](/api/rpc/introduction). ```js const provider = new providers.JsonRpcProvider({ url: `https://rpc.${networkId}.near.org` }); ``` --- ### Access Keys To sign a transaction to send NEAR Ⓝ, we will need a `FullAccess` key to the sender's account. - If you created the account using [`near-cli`](../tools/cli.md) or ran [`near login`](../tools/cli.md#import) in your terminal, your private key can be found in your machine's keychain. - If you created an account using [NEAR Wallet](https://testnet.mynearwallet.com/), your key will be found in your browser's `Local Storage`. - In your browser's dev tools... `Application` >> `Storage` >> `Local Storage` Once you have access to the private key of the sender's account, create an environment variable `SENDER_PRIVATE_KEY` or hard code it as a string on [line 18](https://github.com/near-examples/transaction-examples/blob/master/send-tokens-deconstructed.js#L18) of `send-tokens.js`. - With this `privateKey`, we can now construct a `keyPair` object to sign transactions. ```js const privateKey = process.env.SENDER_PRIVATE_KEY; const keyPair = KeyPair.fromString(privateKey); ``` --- ### Transaction Requirements As stated before, all transactions require six parts: 1. [`signerId`](#1-signerid) 2. [`signerPublicKey`](#2-signerpublickey) 3. [`receiverId`](#3-receiverid) 4. [`nonceForPublicKey`](#4-nonceforpublickey) 5. [`actions`](/protocol/transaction-anatomy#actions) 6. [`blockHash`](#6-blockhash) ### 1 `signerId` - The `signerId` is the account ID of the transaction originator. - This value is passed as a string (ex. `'example.testnet'` or `'bob.near'`) ### 2 `signerPublicKey` - The `signerPublicKey` is required to be an object with two key value pairs: `keyType` and `data`. ```js PublicKey = { keyType: 0, data: Uint8Array(32)[ (190, 150, 152, 145, 232, 248, 128, 151, 167, 165, 128, 46, 20, 231, 103, 142, 39, 56, 152, 46, 135, 1, 161, 180, 94, 212, 195, 201, 73, 190, 70, 242) ], }; ``` - This can be constructed by calling `getPublicKey()` using the `keyPair` we [setup earlier](#access-keys). ```js const publicKey = keyPair.getPublicKey(); ``` ### 3 `receiverId` - The `receiverId` is the account ID of the transaction recipient. - This value is passed as a string (ex. `'example.testnet'` or `'bob.near'`) - The certain cases, the `signerId` and the `receiverId` can be the same account. ### 4 `nonceForPublicKey` - A unique number or `nonce` is required for each transaction signed with an access key. - To ensure a unique number is created for each transaction, the current `nonce` should be queried and then incremented by 1. - Current nonce can be retrieved using the `provider` we [created earlier](#setting-up-a-connection-to-near). ```js const accessKey = await provider.query({ request_type: "view_access_key", finality: "final", account_id: sender, public_key: publicKey.toString(), }); ``` - now we can create a unique number for our transaction by incrementing the current `nonce`. ```js const nonce = ++accessKey.nonce; ``` ### 5 `actions` - There are currently eight supported `Action` types. [[see here]](/protocol/transaction-anatomy#actions) - For this example, we are using `Transfer` - This transfer action can be created using the [imported `transactions` object](#imports) and the [formatted Ⓝ amount](#formatting-token-amounts) created earlier. ```js const actions = [transactions.transfer(amount)]; ``` [[click here]](https://github.com/near/near-api-js/blob/d4d4cf1ac3182fa998b1e004e6782219325a641b/src/transaction.ts#L70-L72) to view source for `transfer()`. ### 6 `blockHash` - Each transaction requires a current block hash (within 24hrs) to prove that the transaction was created recently. - Hash must be converted to an array of bytes using the `base_decode` method found in [`utils`](#imports). ```js const recentBlockHash = utils.serialize.base_decode( accessKey.block_hash ); ``` [[click here]](https://github.com/near/near-api-js/blob/d4d4cf1ac3182fa998b1e004e6782219325a641b/src/utils/serialize.ts#L16-L17) to view source for `base_decode()`. --- ### Constructing the Transaction With all of our [required arguments](#transaction-requirements), we can construct the transaction. - Using [`transactions`](#imports), we call on `createTransaction()` to perform this task. ```js const transaction = transactions.createTransaction( sender, publicKey, receiver, nonce, actions, recentBlockHash ); ``` [[click here]](https://github.com/near/near-api-js/blob/d4d4cf1ac3182fa998b1e004e6782219325a641b/src/transaction.ts#L95-L110) to view source code for the Transaction class --- ### Sign Transaction Now that the transaction is created, we sign it before sending it to the NEAR blockchain. At the lowest level, there are four steps to this process. 1. Using [`utils`](#imports), we call on `serialize()` to serialize the transaction in [Borsh](https://borsh.io/). ```js const serializedTx = utils.serialize.serialize( transactions.SCHEMA.Transaction, transaction ); ``` 2. Hash the serialized transaction using a `sha256` cryptographic hashing algorithm. ```js const serializedTxHash = new Uint8Array(sha256.sha256.array(serializedTx)); ``` 3. Create a signature with the `keyPair`. ```js const signature = keyPair.sign(serializedTxHash); ``` 4. Construct the signed transaction using near-api-js [SignedTransaction class](https://github.com/near/near-api-js/blob/d4d4cf1ac3182fa998b1e004e6782219325a641b/src/transaction.ts#L112-L123). ```js const signedTransaction = new transactions.SignedTransaction({ transaction, signature: new transactions.Signature({ keyType: transaction.publicKey.keyType, data: signature.signature, }), }); ``` ### Send Transaction Final step is to encode and send the transaction. - First we serialize transaction into [Borsh](https://borsh.io/), and store the result as `signedSerializedTx`. _(required for all transactions)_ - Then we send the transaction via [RPC call](/api/rpc/introduction) using the `sendJsonRpc()` method nested inside [`provider`](#setting-up-a-connection-to-near). ```js // encodes transaction to serialized Borsh (required for all transactions) const signedSerializedTx = signedTransaction.encode(); // sends transaction to NEAR blockchain via JSON RPC call and records the result const result = await provider.sendJsonRpc("broadcast_tx_commit", [ Buffer.from(signedSerializedTx).toString("base64"), ]); ``` ### Transaction Results Detailed transaction results of the transaction are returned in the following format: ```bash { status: { SuccessValue: '' }, transaction: { signer_id: 'sender.testnet', public_key: 'ed25519:8RazSLHvzj4TBSKGUo5appP7wVeqZNQYjP9hvhF4ZKS2', nonce: 57, receiver_id: 'receiver.testnet', actions: [ [Object] ], signature: 'ed25519:2sK53w6hybSxX7qWShXz6xKnjnYRUW7Co3evEaaggNW6pGSCNPvx7urY4akwnzAbxZGwsKjx8dcVm73qbitntJjz', hash: 'EgGzB73eFxCwZRGcEyCKedLjvvgxhDXcUtq21SqAh79j' }, transaction_outcome: { proof: [ [Object] ], block_hash: 'J6cFDzAFkuknHMCEYW2uPQXDvCfSndkJmADVEWJbtTwV', id: 'EgGzB73eFxCwZRGcEyCKedLjvvgxhDXcUtq21SqAh79j', outcome: { logs: [], receipt_ids: [Array], gas_burnt: 223182562500, tokens_burnt: '22318256250000000000', executor_id: 'sender.testnet', status: [Object] } }, receipts_outcome: [ { proof: [Array], block_hash: 'FSS7UzTpMr4mUm6aw8MmzP6Q7wnQs35VS8vYm1R461dM', id: '3LjBxe2jq1s7XEPrYxihp4rPVdyHAbYfkcdJjUEVijhJ', outcome: [Object] }, { proof: [Array], block_hash: '4XBio5dM5UGYjJgzZjgckfVgMZ9uKGbTkt8zZi5webxw', id: 'AXFA4kwiYfruKQ4LkD1qZA8P7HoAvtFwGqwQYdWtWNaW', outcome: [Object] } ] } Transaction Results: { signer_id: 'sender.testnet', public_key: 'ed25519:8RazSLHvzj4TBSKGUo5appP7wVeqZNQYjP9hvhF4ZKS2', nonce: 57, receiver_id: 'receiver.testnet', actions: [ { Transfer: [Object] } ], signature: 'ed25519:2sK53w6hybSxX7qWShXz6xKnjnYRUW7Co3evEaaggNW6pGSCNPvx7urY4akwnzAbxZGwsKjx8dcVm73qbitntJjz', hash: 'EgGzB73eFxCwZRGcEyCKedLjvvgxhDXcUtq21SqAh79j' } ``` For detailed information on transaction receipts [[click here]](https://nomicon.io/RuntimeSpec/Receipts.html) - To view the transaction in [NearBlocks Explorer](https://testnet.nearblocks.io/), enter the `hash` located under `transaction` / `Transaction Results`. - In addition, you can create a link in JS using the `networkId` and `result.transaction.hash`. ```js const prefix = (networkId === "testnet") ? "testnet." : ""; const transactionLink = `https://${prefix}nearblocks.io/txns/${result.transaction.hash}`; ``` :::tip Got a question? Ask it on StackOverflow! ::: Happy Coding! 🚀 --- # Source: https://docs.near.org/tutorials/auction/creating-a-frontend.md --- id: creating-a-frontend title: Creating a Frontend description: "Lets create a frontend for our auction using React." --- Now that we have successfully created a contract, it's time to build a frontend to provide a user-friendly interface for interacting with it. Up until now, we have been using the CLI to send transactions and view the contract's state. However, frontends offer a more intuitive way for end users to interact with the contract. They can display all the relevant information in one place, allow users to make calls with a simple button click, and only require a wallet as a prerequisite. --- ## Frontend structure Navigate to the auction frontend. ```bash cd frotends/01-frontend ``` Here we have a simple Next.js frontend that we'll walk through to understand the basics of creating a frontend for a NEAR smart contract. For starters, let's take a look at how the code in the frontend is structured by doing a quick overview of the important files. | File | Description | |----------------------------------|---------------------------------------------------------------------------------| | **_app.js** | Responsible for rending the page, initiates the wallet object and adds it to global context | | **index.js** | The main page where the project's components are loaded into and contains most of the logic for the application like viewing the state of the contract and logic for placing a bid | | **near.js** | Contains the wallet class that has methods to interact with the wallet and blockchain | | **context.js** | Holds the global context - the wallet object and the signed-in account ID - that can be accessed anywhere | | **config.js** | Specifies the account ID of the auction contract | | **Navigation.jsx** | A component that contains a button to sign users in and out of wallets | | **Bid.jsx** | A component allowing a user to make a bid | | **LastBid.jsx** | A component that displays the highest bid and when the highest bid will next refresh | | **Timer.jsx** | A component that shows how long till the auction is over, or, if over, displays a button to claim the auction and then states the auction is over --- ## Specifying the contract We have a config file that specifies the contract name of the auction that the frontend will interact with. There has been an example auction contract deployed and specified already but feel free to change the contract to your own auction contract you deployed. ``` export const AUCTION_CONTRACT = "basic-auction-example.testnet"; // Replace with your contract name export const NetworkId = "testnet"; ``` --- ## Setting up wallets To be able to fully interact with the contract - send bids and claim the auction - you'll need a `wallet` to sign transactions. Wallets securely store your private keys and allow you to sign transactions without exposing your private key to the frontend. The wallet selector allows users to choose between a selection of wallets. We abstract the wallet selector in our `near.js` file by exposing methods to complete various tasks. Feel free to [explore the file](https://github.com/near-examples/auctions-tutorial/blob/main/frontends/01-frontend/src/wallets/near.js) to understand fully how the wallet selector is implemented. The wallet object is initiated in the `app.js` file and its added to the global context along with the account that is signed in to make it easier to access anywhere in the application. ``` const wallet = new Wallet({ networkId: NetworkId }); export default function MyApp({ Component, pageProps }) { const [signedAccountId, setSignedAccountId] = useState(''); useEffect(() => { wallet.startUp(setSignedAccountId) }, []); return ( ); } ``` ``` import { createContext } from 'react'; /** * @typedef NearContext * @property {import('./wallets/near').Wallet} wallet Current wallet * @property {string} signedAccountId The AccountId of the signed user */ /** @type {import ('react').Context} */ export const NearContext = createContext({ wallet: undefined, signedAccountId: '' }); ``` :::tip Access keys On NEAR, in additional to normal full access keys, we have `function-call access keys` that are given to applications to allow them to sign `non-payable` transactions on behalf of the user. This is so the wallet doesn't have to pop up for each non-critical transaction, improving the user experience. When creating the wallet object you can decide to create an access key for the application to use. However, in this example we opt out since the main function - `bid` - we'll be calling is `payable`. You can read further about NEAR's key model [here](../../protocol/access-keys.md). ::: We add a sign-in and sign-out button in the `navigation` component to call the respective methods in the `near.js` file. ``` import Image from 'next/image'; import Link from 'next/link'; import { useEffect, useState, useContext } from 'react'; import { NearContext } from '@/context'; import NearLogo from '/public/near-logo.svg'; export const Navigation = () => { const { signedAccountId, wallet } = useContext(NearContext); const [action, setAction] = useState(() => { }); const [label, setLabel] = useState('Loading...'); useEffect(() => { if (!wallet) return; if (signedAccountId) { setAction(() => wallet.signOut); setLabel(`Logout ${signedAccountId}`); } else { setAction(() => wallet.signIn); setLabel('Login'); } }, [signedAccountId, wallet]); return ( ); }; ``` ``` /** * Displays a modal to login the user */ signIn = async () => { const modal = setupModal(await this.selector, { contractId: this.createAccessKeyFor }); modal.show(); }; /** * Logout the user */ signOut = async () => { const selectedWallet = await (await this.selector).wallet(); selectedWallet.signOut(); }; ``` --- ## Displaying the highest bid To get the highest bid from the auction and who made it we call `get_highest_bid`. Since this function returns the highest bid in `yoctoNEAR` we divide by `10^24` to get the amount in NEAR. ``` const highestBidData = await wallet.viewMethod({ contractId: AUCTION_CONTRACT, method: "get_highest_bid", }); setHighestBid(highestBidData.bid / nearMultiplier) setHighestBidder(highestBidData.bidder) ``` ``` viewMethod = async ({ contractId, method, args = {} }) => { const url = `https://rpc.${this.networkId}.near.org`; const provider = new providers.JsonRpcProvider({ url }); let res = await provider.query({ request_type: 'call_function', account_id: contractId, method_name: method, args_base64: Buffer.from(JSON.stringify(args)).toString('base64'), finality: 'optimistic', }); return JSON.parse(Buffer.from(res.result).toString()); }; ``` In the wallet file, you'll see that we make a query to the RPC provider, since we are not signing a transaction the wallet isn't required here. Here we are using https://rpc.testnet.near.org but note there are [many different providers available](../../api/rpc/providers.md). We are querying the RPC with optimistic finality, which queries the latest block recorded on the node. Alternatively, one could use final finality where the block has been validated by at least 66% of the validators on the network but this will provide slightly delayed information (only by a couple of seconds). We then pass the information about the highest bidder into the `LastBid` component to display the bid amount and the bidder's account Id. ``` {!highestBidder ? : } ``` ``` import styles from './LastBid.module.css'; const LastBid = ({lastBid, highestBidder, lastUpdate}) => { return (
The last bid was {lastBid} $NEAR
Made by {highestBidder} Refresh page in {lastUpdate}
) } export default LastBid ```
--- ## Updating the highest bid We want to know the highest bid at all times, someone else could have placed a higher bid since the page was loaded. To solve this we fetch the contract information every 20 seconds using `setInterval` and update the highest bid if it has changed. In reality you would want to refresh the bid amount more requently but for the sake of saving on RPC calls we are doing it every 20 seconds. ``` const intervalId = setInterval(() => { getInfo(); setSecondsRemaining(20); }, 20000); const countdownIntervalId = setInterval(() => { setSecondsRemaining(prev => (prev === 1 ? 20 : prev - 1)); }, 1000); return () => { clearInterval(intervalId); clearInterval(countdownIntervalId); }; }, []); ``` --- ## Auction end time The contract stores the end time of the auction in the number of nanoseconds since the Unix epoch (1 January 1970 00:00:00 UTC). In our frontend we will display the time left in days, hours, minutes, and seconds. ``` const [time, setTime] = useState((Number(endTime) / 10 ** 6) - Date.now()); useEffect(() => { const timer = setInterval(() => { setTime((prevTime) => { const newTime = prevTime - 1000; if (newTime <= 0) { clearInterval(timer); return 0; } return newTime; }); }, 1000); return () => clearInterval(timer); }, []); const formatTime = (time) => { const allSeconds = Math.floor(time / 1000); const days = Math.floor(allSeconds / (3600 * 24)); const hours = Math.floor((allSeconds % (3600 * 24)) / 3600); const minutes = Math.floor((allSeconds % 3600) / 60); const seconds = allSeconds % 60; return { allSeconds, days, hours, minutes, seconds }; }; ``` --- ## Making a bid To make a bid we make a call to the contract using the `bid` function. We specify the deposit amount in `yoctoNEAR` which will be the bid amount. The input box will take the bid amount in NEAR so we multiply by `10^24` to get the correct amount to send. We also specify the amount of gas to attach to the transaction, here we are attaching 30Tgas which is more than enough for the transaction to go through, we are refunded any unused gas anyway. Here, since the user is changing the state of the contract, not just viewing it, the user needs to sign the transaction. Thus the wallet will pop up displaying the transaction details. ```
{!highestBid ? : }
{!auctionEndTime ? : } {!highestBidder ? : }
); } ``` ``` callMethod = async ({ contractId, method, args = {}, gas = THIRTY_TGAS, deposit = NO_DEPOSIT }) => { // Sign a transaction with the "FunctionCall" action const selectedWallet = await (await this.selector).wallet(); const outcome = await selectedWallet.signAndSendTransaction({ receiverId: contractId, actions: [ { type: 'FunctionCall', params: { methodName: method, args, gas, deposit, }, }, ], }); return providers.getTransactionLastResult(outcome); }; ```
--- ## Claiming the auction Once the auction is over (the current time is greater than the end time) the auction can be claimed. At this point, the timer will be hidden and a button to claim the auction will be displayed. Once clicked the `claim` function will be called on the auction contract to send the highest bidder the NFT and the auctioneer the FTs. ``` const claim = async () => { let response = await wallet.callMethod({ contractId: AUCTION_CONTRACT, method: "claim", gas:"300000000000000" }) return response } ``` --- ## Conclusion In this part of the tutorial, we have implemented a simple frontend for a NEAR contract. Along the way, you have learned how to use the wallet selector to sign the user in and out, how to view the contract’s state, how to sign and send transactions. While we can see the highest bid, we may want to see the auction's bidding history. Since the contract only stores the most recent bid (to reduce storage costs), we need to use an indexer to pull historical data. In the [next part](./2.2-indexing.md) of the tutorial, we'll look at querying historical data using an API endpoint. --- # Source: https://docs.near.org/data-infrastructure/tutorials/running-near-lake/credentials.md --- title: Credentials id: credentials description: "Learn how to provide AWS credentials to access NEAR Lake data" --- To access the data provided by [NEAR Lake](../../near-lake-framework.md) you need to provide valid AWS credentials in order to be charged by the AWS for the S3 usage. :::info AWS credentials Please note that using your own AWS Credentials is the only way to access the data provided by [NEAR Lake](../../near-lake-framework.md) ecosystem. :::
### AWS S3 Credentials To be able to get objects from the AWS S3 bucket you need to provide your AWS credentials. AWS default profile configuration with aws configure looks similar to the following: ``` ~/.aws/credentials ``` ``` [default] aws_access_key_id= aws_secret_access_key= ``` [AWS docs: Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) #### Environment variables Alternatively, you can provide your AWS credentials via environment variables with constant names: ``` $ export AWS_ACCESS_KEY_ID= $ AWS_SECRET_ACCESS_KEY= $ AWS_DEFAULT_REGION=eu-central-1 ``` --- # Source: https://docs.near.org/smart-contracts/anatomy/crosscontract.md --- id: crosscontract title: Cross-Contract Calls description: "Contract can interact with other contracts on the network" --- NEAR contracts can interact with other deployed contracts, querying information and executing functions on them through cross-contract calls. Since NEAR is a sharded blockchain, its cross-contract calls behave differently than in other chains. In NEAR, cross-contract calls are **asynchronous** and **independent**. :::tip Asynchronous The **calling function** and the **callback** execute in **different blocks** (typically 1-2 blocks apart). During this time, the contract remains active and can receive other calls. ::: :::tip Independent Each function — the one making the call, the external function, and the callback — executes in its own context. If the external call fails, the calling function has already completed successfully; there's no automatic rollback. You must handle failures explicitly in the callback. ::: --- ## Snippet: Querying Information While making your contract, it is likely that you will want to query information from another contract. Below, you can see a basic example in which we query the greeting message from our [Hello NEAR](../quickstart.md) example. ``` // Public - query external greeting pub fn ll_query_greeting(&self) -> Promise { // Create a promise to call HelloNEAR.get_greeting() let hello_promise = Promise::new(self.hello_account.clone()).function_call( "get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, TEN_TGAS, ); hello_promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(TEN_TGAS) .ll_query_greeting_callback(), ) } #[private] // Public - but only callable by env::current_account_id() pub fn ll_query_greeting_callback( &self, #[callback_result] call_result: Result, ) -> String { // Check if the promise succeeded by calling the method outlined in external.rs if call_result.is_err() { log!("There was an error contacting Hello NEAR"); return "".to_string(); } // Return the greeting let greeting: String = call_result.unwrap(); greeting } ``` ``` // Public - query external greeting pub fn hl_query_greeting(&self) -> Promise { // Create a promise to call HelloNEAR.get_greeting() let promise = hello_near::ext(self.hello_account.clone()) .with_static_gas(FIVE_TGAS) .get_greeting(); promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(FIVE_TGAS) .hl_query_greeting_callback(), ) } #[private] // Public - but only callable by env::current_account_id() pub fn hl_query_greeting_callback( &self, #[callback_result] call_result: Result, ) -> String { // Check if the promise succeeded by calling the method outlined in external.rs if call_result.is_err() { log!("There was an error contacting Hello NEAR"); return "".to_string(); } // Return the greeting let greeting: String = call_result.unwrap(); greeting } ``` The high level API makes use of the interface defined in the [ext_contract.rs](https://github.com/near-examples/cross-contract-calls/blob/main/contract-simple-rs/src/external_contract.rs) ``` hello_account: AccountId = "hello-nearverse.testnet"; @initialize({}) init({ hello_account }: { hello_account: AccountId }) { this.hello_account = hello_account; } @call({}) query_greeting(): NearPromise { const promise = NearPromise.new(this.hello_account) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, THIRTY_TGAS) .then( NearPromise.new(near.currentAccountId()) .functionCall( "query_greeting_callback", NO_ARGS, NO_DEPOSIT, THIRTY_TGAS, ), ); return promise.asReturn(); } @call({ privateFunction: true }) query_greeting_callback(): String { let { result, success } = promiseResult(); if (success) { return result.substring(1, result.length - 1); } else { near.log("Promise failed..."); return ""; } } ``` ```python from near_sdk_py import call, view, Contract, callback, PromiseResult, CrossContract, init class CrossContractExample(Contract): # Contract we want to interact with hello_contract = "hello-near.testnet" @init def new(self): """Initialize the contract""" # Any initialization logic goes here pass @view def query_greeting_info(self): """View function showing how to make a cross-contract call""" # Create a reference to the Hello NEAR contract # This is a simple call that will execute in the current transaction hello = CrossContract(self.hello_contract) return hello.call("get_greeting").value() @call def query_greeting(self): """Calls Hello NEAR contract to get the greeting with a callback""" # Create a reference to the Hello NEAR contract hello = CrossContract(self.hello_contract) # Call get_greeting and chain a callback # The Promise API handles serialization and callback chaining promise = hello.call("get_greeting").then("query_greeting_callback") return promise.value() @callback def query_greeting_callback(self, result: PromiseResult): """Processes the greeting result from Hello NEAR contract""" # The @callback decorator automatically parses the promise result # result will have a data property and a success boolean if not result.success: return {"success": False, "message": "Failed to get greeting"} return { "success": True, "greeting": result.data, "message": f"Successfully got greeting: {result.data}" } ``` ```go package main "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} // @contract:payable min_deposit=0.001NEAR func (c *Contract) ExampleQueryingGreetingInfo() { helloAccount := "hello-nearverse.testnet" gas := uint64(10 * types.ONE_TERA_GAS) promise.NewCrossContract(helloAccount). Gas(gas). Call("get_greeting", map[string]string{}). Value() } // @contract:payable min_deposit=0.001NEAR func (c *Contract) ExampleQueryingInformation() { helloAccount := "hello-nearverse.testnet" gas := uint64(10 * types.ONE_TERA_GAS) promise.NewCrossContract(helloAccount). Gas(gas). Call("get_greeting", map[string]string{}). Then("example_querying_information_response", map[string]string{}) } // @contract:view // @contract:promise_callback func (c *Contract) ExampleQueryingInformationResponse(result promise.PromiseResult) { if result.Success { env.LogString("State change/Query completed successfully") } else { env.LogString("State change/Query failed") } env.LogString("Promise result status: " + types.IntToString(result.StatusCode)) if len(result.Data) > 0 { env.LogString("Returned data: " + string(result.Data)) } else { env.LogString("No return data") } } ``` --- ## Snippet: Sending Information Calling another contract passing information is also a common scenario. Below you can see a function that interacts with the [Hello NEAR](../quickstart.md) example to change its greeting message. ``` // Public - change external greeting pub fn ll_change_greeting(&mut self, new_greeting: String) -> Promise { let args = json!({ "greeting": new_greeting }).to_string().into_bytes(); let hello_promise = Promise::new(self.hello_account.clone()).function_call( "set_greeting".to_owned(), args, NO_DEPOSIT, TEN_TGAS, ); hello_promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(TEN_TGAS) .ll_change_greeting_callback(), ) } #[private] pub fn ll_change_greeting_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, ) -> bool { // Return whether or not the promise succeeded using the method outlined in external.rs if call_result.is_err() { env::log_str("set_greeting failed..."); false } else { env::log_str("set_greeting was successful!"); true } } } ``` ``` // Public - change external greeting pub fn hl_change_greeting(&mut self, new_greeting: String) -> Promise { // Create a promise to call HelloNEAR.set_greeting(message:string) hello_near::ext(self.hello_account.clone()) .with_static_gas(FIVE_TGAS) .set_greeting(new_greeting) .then( // Create a callback change_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(FIVE_TGAS) .hl_change_greeting_callback(), ) } #[private] pub fn hl_change_greeting_callback( &mut self, #[callback_result] call_result: Result<(), PromiseError>, ) -> bool { // Return whether or not the promise succeeded using the method outlined in external.rs if call_result.is_err() { env::log_str("set_greeting failed..."); false } else { env::log_str("set_greeting was successful!"); true } } } ``` The high level API makes use of the interface defined in the [ext_contract.rs](https://github.com/near-examples/cross-contract-calls/blob/main/contract-simple-rs/src/external_contract.rs) ``` @call({}) change_greeting({ new_greeting }: { new_greeting: string }): NearPromise { const promise = NearPromise.new(this.hello_account) .functionCall( "set_greeting", JSON.stringify({ greeting: new_greeting }), NO_DEPOSIT, THIRTY_TGAS, ) .then( NearPromise.new(near.currentAccountId()) .functionCall( "change_greeting_callback", NO_ARGS, NO_DEPOSIT, THIRTY_TGAS, ), ); return promise.asReturn(); } @call({ privateFunction: true }) change_greeting_callback(): boolean { let { success } = promiseResult(); if (success) { near.log(`Success!`); return true; } else { near.log("Promise failed..."); return false; } } } ``` ```python from near_sdk_py import call, Contract, callback, PromiseResult, CrossContract class CrossContractExample(Contract): # Contract we want to interact with hello_contract = "hello-near.testnet" @call def change_greeting(self, new_greeting): """Changes the greeting on the Hello NEAR contract""" # Create a reference to the Hello NEAR contract hello = CrossContract(self.hello_contract) # Create a promise to call set_greeting with the new greeting # Pass context data to the callback directly as kwargs promise = hello.call( "set_greeting", message=new_greeting ).then( "change_greeting_callback", original_greeting=new_greeting # Additional context passed to callback ) return promise.value() @callback def change_greeting_callback(self, result: PromiseResult, original_greeting): """Processes the result of set_greeting""" # The original_greeting parameter is passed from the change_greeting method if not result.success: return { "success": False, "message": f"Failed to set greeting to '{original_greeting}'" } return { "success": True, "message": f"Successfully set greeting to '{original_greeting}'", "result": result.data } ``` ```go package main "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} // @contract:payable min_deposit=0.00001NEAR func (c *Contract) ExampleSendingInformation() { helloAccount := "hello-nearverse.testnet" gas := uint64(30 * types.ONE_TERA_GAS) args := map[string]string{ "message": "New Greeting", } promise.NewCrossContract(helloAccount). Gas(gas). Call("set_greeting", args). Then("example_change_greeting_callback", map[string]string{}) } // @contract:view // @contract:promise_callback func (c *Contract) ExampleChangeGreetingCallback(result promise.PromiseResult) { if result.Success { env.LogString("State change completed successfully") } else { env.LogString("State change failed") } env.LogString("Promise result status: " + types.IntToString(int(result.StatusCode))) if len(result.Data) > 0 { env.LogString("Returned data: " + string(result.Data)) } else { env.LogString("No return data from state change") } } ``` --- ## Promises Cross-contract calls work by creating two promises in the network: 1. A promise to execute code in the external contract - `Promise.create` 2. **Optional**: A promise to call another function with the result - `Promise.then` Both promises will contain the following information: - The **address** of the contract you want to interact with - The **function** that you want to execute - The arguments to pass to the function - The amount of **GAS** to use (deducted from the **attached Gas**) - The NEAR **deposit** to attach (deducted from **your contract's balance**) :::tip The callback can be made to **any** contract. Meaning that the result could potentially be handled by another contract ::: --- ## Creating a Cross Contract Call To create a cross-contract call with a callback, create two promises and use the `.then` method to link them: ```rust #[ext_contract(external_trait)] trait Contract { fn function_name(&self, param1: T, param2: T) -> T; } external_trait::ext("external_address") .with_attached_deposit(DEPOSIT) .with_static_gas(GAS) .function_name(arguments) .then( // this is the callback Self::ext(env::current_account_id()) .with_attached_deposit(DEPOSIT) .with_static_gas(GAS) .callback_name(arguments) ); ``` ```rust let arguments = json!({ "foo": "bar" }) .to_string() .into_bytes(); let promise = Promise::new("external_address").function_call( "function_name".to_owned(), arguments, DEPOSIT, GAS ); promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(GAS) .callback_name(), ); ``` ```ts NearPromise.new("external_address").functionCall("function_name", JSON.stringify(arguments), DEPOSIT, GAS) .then( // this function is the callback NearPromise.new(near.currentAccountId()).functionCall("callback_name", JSON.stringify(arguments), DEPOSIT, GAS) ); ``` ```go package main import ( "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type PromiseCallbackInputData struct { Data string `json:"data"` } // @contract:payable min_deposit=0.00001NEAR func (c *Contract) ExampleCrossContractCall() { externalAccount := "hello-nearverse.testnet" gas := uint64(5 * types.ONE_TERA_GAS) args := map[string]string{ "message": "New Greeting", } callback_args := map[string]string{ "data": "saved_for_callback", } promise.NewCrossContract(externalAccount). Gas(gas). Call("set_greeting", args). Then("example_cross_contract_callback", callback_args). Value() } // @contract:view // @contract:promise_callback func (c *Contract) ExampleCrossContractCallback(input PromiseCallbackInputData, result promise.PromiseResult) { env.LogString("Executing callback") env.LogString("Input CrossContractCallback : " + input.Data) if result.Success { env.LogString("Cross-contract call executed successfully") } else { env.LogString("Cross-contract call failed") } } ```
Concatenating Promises ✅ You can concatenate promises: `P1.then(P2).then(P3)`: `P1` executes, then `P2` executes with the result of `P1`, then `P3` executes with the result of `P2` ✅ You can join promises: `(P1.and(P2)).then(P3)`: `P1` and `P2` execute in parallel, after they finish `P3` will execute and have access to **both their results** ⛔ You cannot **return** a joint promise without a callback: `return P1.and(P2)` is invalid, you need to add a `.then()` ⛔ You cannot join promises within a `then`: `P1.then(P2.join([P3]))` is invalid ⛔ You cannot use a `then` within a `then`: `P1.then(P2.then(P3))` is invalid
:::info If a function returns a promise, then it will delegate the return value and status of transaction execution, but if you return a value or nothing, then the `Promise` result will not influence the transaction status ::: :::caution The Promises you are creating will **not execute immediately**. In fact, they will be queued in the network an: - The cross-contract call will execute 1 or 2 blocks after your function finishes **correctly**. ::: --- ## Callback Function If your function finishes correctly, then eventually your callback function will execute. This will happen whether the **external contract fails or not**. In the callback function you will have access to the result, which will contain the status of the external function (if it worked or not), and the values in case of success. ``` #[private] // Public - but only callable by env::current_account_id() pub fn hl_query_greeting_callback( &self, #[callback_result] call_result: Result, ) -> String { // Check if the promise succeeded by calling the method outlined in external.rs if call_result.is_err() { log!("There was an error contacting Hello NEAR"); return "".to_string(); } // Return the greeting let greeting: String = call_result.unwrap(); greeting } ``` ``` @call({ privateFunction: true }) query_greeting_callback(): String { let { result, success } = promiseResult(); if (success) { return result.substring(1, result.length - 1); } else { near.log("Promise failed..."); return ""; } } @call({}) ``` ```python from near_sdk_py import callback, PromiseResult, Contract class CrossContractExample(Contract): @callback def query_greeting_callback(self, result: PromiseResult, additional_context=None): """ Process the result of a cross-contract call. The @callback decorator automatically: 1. Reads the promise result data 2. Handles serialization/deserialization 3. Provides proper error handling Parameters: - result: The PromiseResult object with status and data - additional_context: Optional context passed from the calling function """ if not result.success: # This means the external call failed or returned nothing return { "success": False, "message": "Failed to get greeting", "context": additional_context } # Process successful result return { "success": True, "greeting": result.data, "message": f"Successfully got greeting: {result.data}", "context": additional_context } ``` ```go type PromiseCallbackInputData struct { Data string `json:"data"` } // @contract:view // @contract:promise_callback func (c *Contract) ExampleCrossContractCallback(input PromiseCallbackInputData, result promise.PromiseResult) { env.LogString("Executing callback") env.LogString("Input CrossContractCallback : " + input.Data) if result.Success { env.LogString("Cross-contract call executed successfully") } else { env.LogString("Cross-contract call failed") } } ``` :::info Callback with always execute We repeat, if your function finishes correctly, then your callback will **always execute**. This will happen no matter if the external function finished correctly or not ::: :::warning Always make sure to have enough Gas for your callback function to execute ::: :::tip Remember to mark your callback function as private using macros/decorators, so it can only be called by the contract itself :::
### What happens if the function I call fails? If the external function fails (i.e. it panics), then your callback will be **executed anyway**. Here you need to **manually rollback** any changes made in your contract during the original call. Particularly: 1. **Refund the predecessor** if needed: If the contract attached NEAR to the call, the funds are now back in **the contract's account** 2. **Revert any state changes**: If the original function made any state changes (i.e. changed or stored data), you need to manually roll them back. **They won't revert automatically** :::warning If your original function finishes correctly then the callback executes **even if the external function panics**. Your state will **not** rollback automatically, and $NEAR will **not** be returned to the signer automatically. Always make sure to check in the callback if the external function failed, and manually rollback any operation if necessary. ::: --- ## Calling Multiple Functions on the Same Contract You can call multiple functions in the same external contract, which is known as a **batch call**. An important property of batch calls is that they **act as a unit**: they execute in the same [receipt](/protocol/transaction-execution#receipts--finality), and if **any function fails**, then they **all get reverted**. ``` pub fn batch_actions(&mut self) -> Promise { let hi = json!({ "greeting": "hi" }).to_string().into_bytes(); let bye = json!({ "greeting": "bye" }).to_string().into_bytes(); // You can create one transaction calling multiple methods // on a same contract Promise::new(self.hello_account.clone()) .function_call("set_greeting".to_owned(), hi, NO_DEPOSIT, XCC_GAS) .function_call("get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS) .function_call("set_greeting".to_owned(), bye, NO_DEPOSIT, XCC_GAS) .function_call("get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS) .then(Self::ext(env::current_account_id()).batch_actions_callback()) } ``` ``` export function batch_actions(accountId: AccountId) { const promise = NearPromise.new(accountId) .functionCall("set_greeting", JSON.stringify({ greeting: 'hi' }), NO_DEPOSIT, TEN_TGAS) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS) .functionCall("set_greeting", JSON.stringify({ greeting: 'bye' }), NO_DEPOSIT, TEN_TGAS) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS) .then( NearPromise.new(near.currentAccountId()) .functionCall("batch_actions_callback", NO_ARGS, NO_DEPOSIT, TEN_TGAS) ) return promise.asReturn(); }; ``` ```python from near_sdk_py import call, Context, Contract, callback, PromiseResult, ONE_TGAS, CrossContract, init class BatchCallsExample(Contract): # Contract we want to interact with hello_contract = "hello-near.testnet" @init def new(self): """Initialize the contract""" pass @call def call_multiple_methods(self, greeting1, greeting2): """Call multiple methods on the same contract in a batch""" # Create a contract instance hello = CrossContract(self.hello_contract) # Create a batch for the hello contract batch = hello.batch() # Add function calls to the batch batch.function_call("set_greeting", {"message": greeting1}) batch.function_call("another_method", {"arg1": greeting2}) # Add a callback to process the result promise = batch.then(Context.current_account_id()).function_call( "batch_callback", {"original_data": [greeting1, greeting2]}, gas=10 * ONE_TGAS ) return promise.value() @callback def batch_callback(self, result: PromiseResult, original_data=None): """Process batch result - only gets the result of the last operation""" return { "success": result.success, "result": result.data, "original_data": original_data } ``` ```go package main "strconv" "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type PromiseCallbackInputData struct { Data string `json:"data"` } // @contract:payable min_deposit=0.00001NEAR func (c *Contract) ExampleBatchCallsSameContract() { helloAccount := "hello-nearverse.testnet" gas := uint64(10 * types.ONE_TERA_GAS) amount, _ := types.U128FromString("0") callback_args := map[string]string{ "data": "[Greeting One, Greeting Two]", } promise.NewCrossContract(helloAccount). Batch(). Gas(gas). FunctionCall("set_greeting", map[string]string{ "message": "Greeting One", }, amount, gas). FunctionCall("another_method", map[string]string{ "arg1": "val1", }, amount, gas). Then(helloAccount). FunctionCall("example_batch_calls_callback", callback_args, amount, gas) env.LogString("Batch call created successfully") } // @contract:view // @contract:promise_callback func (c *Contract) ExampleBatchCallsCallback(input PromiseCallbackInputData, result promise.PromiseResult) { env.LogString("Processing batch call results") env.LogString("Input CrossContractCallback : " + input.Data) env.LogString("Batch call success: " + strconv.FormatBool(result.Success)) if len(result.Data) > 0 { env.LogString("Batch call data: " + string(result.Data)) } } ``` :::tip Callbacks only have access to the result of the **last function** in a batch call ::: --- ## Calling Multiple Functions on Different Contracts You can also call multiple functions in **different contracts**. These functions will be executed in parallel, and do not impact each other. This means that, if one fails, the others **will execute, and NOT be reverted**. ``` pub fn multiple_contracts(&mut self) -> Promise { // We create a promise that calls the `get_greeting` function on the HELLO_CONTRACT let hello_promise = Promise::new(self.hello_account.clone()).function_call( "get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS, ); // We create a promise that calls the `get_num` function on the COUNTER_CONTRACT let counter_promise = Promise::new(self.counter_account.clone()).function_call( "get_num".to_owned(), NO_ARGS, NO_DEPOSIT, XCC_GAS, ); // We create a promise that calls the `get_messages` function on the GUESTBOOK_CONTRACT let args = json!({ "from_index": "0", "limit": 2 }) .to_string() .into_bytes(); let guestbook_promise = Promise::new(self.guestbook_account.clone()).function_call( "get_messages".to_owned(), args, NO_DEPOSIT, XCC_GAS, ); // We join all promises and chain a callback to collect their results. hello_promise .and(counter_promise) .and(guestbook_promise) .then( Self::ext(env::current_account_id()) .with_static_gas(XCC_GAS) .multiple_contracts_callback(), ) } ``` ``` export function multiple_contracts(contract: CrossContractCall) { const promise1 = NearPromise.new(contract.hello_account) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, TEN_TGAS) const promise2 = NearPromise.new(contract.counter_account) .functionCall("get_num", NO_ARGS, NO_DEPOSIT, TEN_TGAS) const promise3 = NearPromise.new(contract.guestbook_account) .functionCall("get_messages", NO_ARGS, NO_DEPOSIT, TEN_TGAS) return promise1 .and(promise2) .and(promise3) .then( NearPromise.new(near.currentAccountId()) .functionCall("multiple_contracts_callback", JSON.stringify({ number_promises: 3 }), NO_DEPOSIT, TEN_TGAS) ) }; ``` ```python from near_sdk_py import call, Contract, multi_callback, PromiseResult, CrossContract, init class MultiContractExample(Contract): # Contract addresses we want to interact with contract_a = "contract-a.testnet" contract_b = "contract-b.testnet" @init def new(self): """Initialize the contract""" pass @call def call_multiple_contracts(self): """Calls multiple different contracts in parallel""" # Create promises for each contract contract_a = CrossContract(self.contract_a) promise_a = contract_a.call("method_a") contract_b = CrossContract(self.contract_b) promise_b = contract_b.call("method_b") # Join the promises and add a callback # The first promise's join method can combine multiple promises combined_promise = promise_a.join( [promise_b], "multi_contract_callback", contract_ids=[self.contract_a, self.contract_b] # Context data ) return combined_promise.value() @multi_callback def multi_contract_callback(self, results, contract_ids=None): """Process results from multiple contracts""" # results is an array containing all promise results in order return { "contract_a": { "id": contract_ids[0], "result": results[0].data, "success": results[0].success }, "contract_b": { "id": contract_ids[1], "result": results[1].data, "success": results[1].success }, "success": all(result.success for result in results) } ``` ```go package main "strconv" "github.com/vlmoon99/near-sdk-go/env" "github.com/vlmoon99/near-sdk-go/promise" "github.com/vlmoon99/near-sdk-go/types" ) // @contract:state type Contract struct{} type PromiseCallbackInputData struct { Data string `json:"data"` } // @contract:payable min_deposit=0.00001NEAR func (c *Contract) ExampleParallelCallsDifferentContracts() { contractA := "hello-nearverse.testnet" contractB := "child.neargopromises1.testnet" promiseA := promise.NewCrossContract(contractA). Call("get_greeting", map[string]string{}) promiseB := promise.NewCrossContract(contractB). Call("SetStatus", map[string]string{"message": "Hello, World!"}) promiseA.Join([]*promise.Promise{promiseB}, "example_parallel_contracts_callback", map[string]string{ "data": contractA + "," + contractB, }).Value() env.LogString("Parallel contract calls initialized") } // @contract:view // @contract:promise_callback func (c *Contract) ExampleParallelContractsCallback(input PromiseCallbackInputData, results []promise.PromiseResult) { env.LogString("Processing results from multiple contracts") env.LogString("Input CrossContractCallback : " + input.Data) for i, result := range results { env.LogString("Processing result " + types.IntToString(i)) env.LogString("Success: " + strconv.FormatBool(result.Success)) if len(result.Data) > 0 { env.LogString("Data: " + string(result.Data)) } } env.LogString("Processed " + types.IntToString(len(results)) + " contract responses") } ``` :::tip Callbacks have access to the result of **all functions** in a parallel call ::: --- ## Security Concerns While writing cross-contract calls there is a significant aspect to keep in mind: all the calls are **independent** and **asynchronous**. In other words: - The function in which you make the call and function for the callback are **independent**. - There is a **delay between the call and the callback**, in which people can still interact with the contract This has important implications on how you should handle the callbacks. Particularly: 1. Make sure you don't leave the contract in a exploitable state between the call and the callback. 2. Manually rollback any changes to the state in the callback if the external call failed. We have a whole [security section](../security/callbacks.md) dedicated to these specific errors, so please go and check it. :::warning Not following these basic security guidelines could expose your contract to exploits. Please check the [security section](../security/callbacks.md), and if still in doubt, [join us in Discord](https://near.chat). ::: --- # Source: https://docs.near.org/ai/shade-agents/reference/custom-agent-contract.md --- id: custom-agent-contract title: Custom Agent Contract sidebar_label: Custom Agent Contract description: "Learn how to build, deploy, and interact with custom Shade Agent contracts." --- In some cases, you may want to deploy a `custom agent contract`. This allows for more customizability inside the agent contract, allows you to restrict your agent to certain actions by implementing `guard rails`, and is used for building Shade Agents that interact with just NEAR. --- ## Creating the Contract To create a custom agent contract, we recommend you fork the [sandbox contract](https://github.com/NearDeFi/shade-agent-js/tree/main/contracts/sandbox). This is the agent contract that's deployed under the hood when using the `shade-agent-cli`. For testing locally, you will need to maintain a separate similar contract that does not implement the agent registration flow; for this, you can follow the structure of the [proxy contract](https://github.com/NearDeFi/shade-agent-js/tree/main/contracts/proxy). Inside this contract, you will create your own functions. For building contracts on NEAR, see our [smart contract docs](../../../smart-contracts/quickstart.md). You can only develop agent contracts in Rust. With the quickstart template, an agent can sign any transaction. You may want to limit your agent to only be able to sign transactions for a limited set of functions or actions. For this, we recommend building your transactions inside your agent contract with the [omni-transaction-rs](https://github.com/near/omni-transaction-rs) library. In such case, you should make the `request_signature` function private. For developing Shade Agents that just interact with NEAR, you can remove the `request_signature` function. The agent will make function calls to the contract directly. Make sure that you restrict relevant functions to only be allowed to be called by the agent. If you want to interact with existing contracts on NEAR that do not implement agent registration, you can leverage [cross contract calls](../../../smart-contracts/anatomy/crosscontract.md). How you compile the contract depends on your operating system. For Linux, you can compile the contract directly with [cargo near](https://github.com/near/cargo-near/releases/latest). ```bash cargo near build non-reproducible-wasm ``` Because of a required dependency used in the agent registration flow, agent contracts cannot easily be compiled on a Mac. We suggest you build the contract inside a Docker container. You can use this pre-configured image for compiling NEAR contracts: ```bash docker run --rm -v "$(pwd)":/workspace pivortex/near-builder@sha256:cdffded38c6cff93a046171269268f99d517237fac800f58e5ad1bcd8d6e2418 cargo near build non-reproducible-wasm ``` --- ## Deploying the Custom Contract To deploy your agent with the custom contract, add the `--wasm` flag when using the Shade Agent CLI, specifying the path to your wasm file. Depending on the size of the wasm, you may require more NEAR to deploy the contract. This can be done using the `--funding` flag, followed by the amount of NEAR (100KB = 1 NEAR). Here is an example: ```bash shade-agent-cli --wasm contract/target/near/contract.wasm --funding 5 ``` --- ## Interacting with Custom Contract To call a function on your custom agent contract, use the `agentCall` function provided by the Shade Agent API. Please refer to the [relevant docs](./api.md#agent-call) for calling the contract in your desired language. --- # Source: https://docs.near.org/ai/shade-agents/tutorials/ai-dao/dao-agent-contract.md --- id: dao-agent-contract title: DAO Agent Contract sidebar_label: DAO Agent Contract description: "Learn about the key parts of the agent contract as part of the Verifiable AI DAO Shade Agent tutorial, including how to create a custom agent contract and create a yield and resume-based Shade Agent." --- On this page, you'll look at the DAO smart contract that uses the yield and resume pattern to enable the Shade Agent to vote on proposals within a `single transaction` flow. The AI DAO contract is a fork of the [default agent contract](https://github.com/NearDeFi/shade-agent-js/tree/main/contracts/sandbox), modified to remove the `request_signature` function and implement DAO-specific functionality. This page focuses on the DAO-specific code, as agent registration follows the default contract. --- ## Contract Structure The DAO agent contract extends the default contract with additional state: - The DAO's manifesto - A map of pending proposals - A map of finalized proposals - The current proposal ID ``` pub struct Contract { pub owner_id: AccountId, pub approved_codehashes: IterableSet, pub worker_by_account_id: IterableMap, pub manifesto: Manifesto, pub pending_proposals: IterableMap, pub finalized_proposals: IterableMap, pub current_proposal_id: u32, } ``` ### Manifesto The manifesto consists of two components: the `manifesto text` that defines the DAO's decision-making principles and a `hash` of the manifesto for verifying that the agent uses the correct manifesto when voting. ``` pub struct Manifesto { pub manifesto_text: String, pub manifesto_hash: String, } ``` The manifesto and its hash are initialized as empty strings. ### Pending Proposals This stores all proposals that are awaiting a vote from the agent. Each proposal request includes the `proposal text` and `yield ID` - a unique identifier for each yielded promise (each active pending proposal request). ``` pub struct ProposalRequest { pub yield_id: CryptoHash, pub proposal_text: String, } ``` The map is initialized as empty. ### Finalized Proposals This stores all proposals that the agent has voted on. Each finalized proposal contains the `proposal text`, `proposal result` (enum of `Approved` or `Rejected`), and `reasoning` for the vote. The result and reasoning are provided by the agent. ``` pub struct FinalizedProposal { pub proposal_text: String, pub proposal_result: ProposalResult, pub reasoning: String, } ``` ``` pub enum ProposalResult { Approved, Rejected, } ``` The map is initialized as empty. ### Current Proposal ID The current proposal ID is an integer identifier that increments with each proposal request and is used to identify different proposals. If a proposal is not voted on by the agent, then the proposal ID will still increment, leading to `non-consecutive proposal IDs` within the finalized proposals map. Note that the proposal ID is different to the yield ID. --- ## Setting the Manifesto The contract provides a function to set the manifesto, which only the contract `owner` can call. The owner provides the manifesto text, which is `hashed` and stored along with the text in the contract's state. In production, the owner would typically be a `multisig` contract. ``` pub fn set_manifesto(&mut self, manifesto_text: String) { self.require_owner(); require!( manifesto_text.len() <= 10000, "Manifesto text needs to be under 10,000 characters" ); self.manifesto = Manifesto { manifesto_text: manifesto_text.clone(), manifesto_hash: hash(manifesto_text), }; } ``` --- ## Creating a Proposal When a user calls `create_proposal` with the proposal text, the function creates a `yielded promise`. The promise will call the specified function,`return_external_response`, with the arguments of `proposal_id` and `proposal_text` when the promise resolves. The promise resolves when the agent produces a valid response or the promise times out - after 200 blocks (~2 minutes). ``` let yielded_promise = env::promise_yield_create( "return_external_response", // Function to call when the promise is resumed &json!({ "proposal_id": proposal_id, "proposal_text": proposal_text }) .to_string() .into_bytes(), RETURN_EXTERNAL_RESPONSE_GAS, GasWeight::default(), YIELD_REGISTER, ); ``` The function then reads the `yield ID` from the `register` for the created promise. The `yield ID` is a unique hash identifier that ensures responses are matched to the correct pending proposal. The yield ID is generated by the `register`, which takes an integer identifier that specifies which register is being used, since there is just one yield-resume register here, it's set to zero. ``` let yield_id: CryptoHash = env::read_register(YIELD_REGISTER) .expect("read_register failed") .try_into() .expect("conversion to CryptoHash failed"); ``` A new proposal request is created and inserted into the pending proposals map, allowing the agent to fetch proposals that it needs to respond to. ``` let proposal_request = ProposalRequest { yield_id, proposal_text: proposal_text.clone(), }; self.pending_proposals.insert(proposal_id, proposal_request); ``` Lastly, the function `returns the yielded promise` making it ready to be resumed. ``` env::promise_return(yielded_promise) } ``` --- ## Agent Response and Validation Once the agent makes its decision, it calls the `agent_vote` function. This function checks whether the response is valid and resumes the yielded promise if so. The agent responds with the `yield ID` for the promise it intends to resume, the `proposal ID` it's voting on, the `hash of the proposal`, the `hash of the manifesto`, the `vote`, and the `reasoning` behind the vote. ``` pub fn agent_vote(&mut self, yield_id: CryptoHash, response: AiResponse) { // Comment this out for local development ``` ``` pub struct AiResponse { proposal_id: u32, proposal_hash: String, manifesto_hash: String, vote: ProposalResult, reasoning: String, } ``` Most importantly, the function checks if the caller is a `valid registered agent`, ensuring the DAO only makes decisions through the expected process (that's defined by the verifiable agent). ``` self.require_approved_codehash(); ``` The function verifies that the `manifesto hash` and `proposal hash` submitted by the agent match those stored in the contract. This verification ensures the agent used the correct manifesto and proposal when voting, `removing trust in the RPC` used to fetch proposals and manifesto data. Otherwise, there could be a bug in the RPC causing it to fetch the wrong details and the RPC or a malicious intermediary could intentionally provide the wrong details to try to corrupt the vote. ``` require!( response.manifesto_hash == self.manifesto.manifesto_hash, "Manifesto hash mismatch" ); // Verify the proposal exists and hash matches let pending_proposal = self .pending_proposals .get(&response.proposal_id) .expect("Proposal not found or already processed"); require!( response.proposal_hash == hash(pending_proposal.proposal_text.clone()), "Proposal hash mismatch" ); ``` If any of these checks fail then the function panics and the promise is not be resumed (it could be resumed later with valid arguments before timeout). If all checks pass, then the function resumes the promise with the specified yield ID and the agent's response as an argument. ``` env::promise_yield_resume(&yield_id, &serde_json::to_vec(&response).unwrap()); } ``` --- ### Proposal Finalization When the yielded promise resolves (either resumed or timed out), the `return_external_response` function is called. This function is private and can only be called by the yielded promise, not by external accounts. The function receives arguments from both when the promise was created and when it was resumed. ``` #[private] pub fn return_external_response( &mut self, proposal_id: u32, proposal_text: String, #[callback_result] response: Result, ) -> PromiseOrValue { self.pending_proposals.remove(&proposal_id); ``` The function first removes the proposal being responded to from the pending proposals map, regardless of whether the promise was resumed or timed out. ``` self.pending_proposals.remove(&proposal_id); ``` If the response is valid, i.e. the yielded promise was successfully resumed, the proposal and the result are added to the map of finalized proposals, and a response is returned to the caller within the same transaction that the proposal was submitted in. ``` match response { Ok(ai_response) => { // Add to finalized proposals and return the decision let finalized_proposal = FinalizedProposal { proposal_text, proposal_result: ai_response.vote.clone(), reasoning: ai_response.reasoning.clone(), }; self.finalized_proposals .insert(proposal_id, finalized_proposal); PromiseOrValue::Value(DaoResponse { vote: ai_response.vote, reasoning: ai_response.reasoning, }) } Err(_) => { ``` If the response is invalid, i.e. the yielded promise timed out, the function returns a promise to call `fail_on_timeout`, which panics and produces a failed [receipt](../../../../protocol/transaction-execution) in a separate block to provide a clear error to the user (the return_external_response receipt is still successful). ``` Err(_) => { // Make a call to fail_on_timeout to cause a failed receipt let promise = Promise::new(env::current_account_id()).function_call( "fail_on_timeout".to_string(), vec![], NearToken::from_near(0), FAIL_ON_TIMEOUT_GAS, ); PromiseOrValue::Promise(promise.as_return()) } } ``` ``` #[private] pub fn fail_on_timeout(&self) { env::panic_str("Proposal request timed out"); } ``` :::tip Visit the [yield and resume section](../../../../smart-contracts/anatomy/yield-resume.md) of the docs for a deeper look into this pattern. ::: --- ## View Functions The contract exposes [view functions](https://github.com/NearDeFi/verifiable-ai-dao/blob/main/contract/src/dao.rs#L188-L223) to retrieve the manifesto text, pending proposals, and finalized proposals. --- ## Next steps Now that you understand the DAO agent contract implementation, continue to the [agent page](./dao-agent.md) to learn about the verifiable agent that queries the contract for pending requests and casts a vote using an LLM. --- # Source: https://docs.near.org/ai/shade-agents/tutorials/ai-dao/dao-agent.md --- id: dao-agent title: DAO Agent sidebar_label: DAO Agent description: "Learn about the key parts of the agent as part of the Verifiable AI DAO Shade tutorial that walks through how to index the agent contract, using verifiable AI, and interacting with the custom agent contract." --- On this page, you'll examine the agent component of the DAO. The agent continuously monitors for new proposals, uses an LLM to evaluate them, and submits its vote along with reasoning back to the smart contract. --- ## Starting the Agent Before an agent can execute any actions on-chain, it must first be `registered`. When in production (running on Phala Cloud, not locally), the agent runs a loop to check its registration status. An agent can see if it's registered using the `agentInfo` function provided by `shade-agent-js`; once registered, `agentInfo` will return a checksum. After the agent is registered, it starts the `responder`, which contains the core logic of the agent. ``` if (process.env.NODE_ENV === "production") { // If in production wait until agent is registered to start the responder while (true) { await new Promise(resolve => setTimeout(resolve, 10000)); console.log("Looping check if registered") try { const res = await agentInfo(); const checksum = res.checksum; if (checksum && checksum !== null && checksum !== undefined) { break; } } catch (error) { console.error("Error in checksum loop:", error); } } } console.log("Starting responder"); responder(); } ``` --- ## Indexing Proposals Once started, the responder begins a continuous loop to check for pending proposals by calling `get_pending_proposals` on the contract using the `agentView` function provided by `shade-agent-js`. The `agentView` function makes a view call (a gasless transaction that does not change the contract's state) to the selected function on the agent contract. ``` // Fetch the pending proposals const requests: [number, ProposalRequest][] = await agentView({ methodName: "get_pending_proposals", args: {} }); // If there are no pending proposals restart the loop if (requests.length === 0) { console.log("No pending proposals"); continue; } ``` If proposals are found, the agent extracts the `proposal text` and `yield ID` from the oldest pending proposal , then fetches the current manifesto from the DAO by calling `get_manifesto`. ``` // Extract the proposal text and yield id from the oldest proposal const proposal_to_respond_to: [number, ProposalRequest] = requests[0]; const proposal_id: number = proposal_to_respond_to[0]; const yield_id: string = proposal_to_respond_to[1].yield_id; const proposal_text: string = proposal_to_respond_to[1].proposal_text; // Fetch the manifesto const manifesto_text: string = await agentView({ methodName: "get_manifesto", args: {} }); ``` Having retrieved both the proposal and manifesto, the agent is ready to make its decision using an LLM. --- ## Voting with an LLM To make a decision on the proposal, the agent uses an LLM provided by [NEAR AI](https://docs.near.ai/cloud/get-started/). NEAR AI provides verifiable and private inference by running LLMs in GPU TEEs. In this tutorial, the DAO uses NEAR AI for its `verifiable` component. This allows the agent verify that no one is interfering with the LLM response, as could happen with centralized model hosting. The agent knows the response from the LLM is actually a function of the input, and comes from the expected model. :::note In this tutorial, the agent does not actually verify the attestation from the LLM. Full verification will be added in a future update to the tutorial. ::: The DAO uses the `Open AI SDK` to interact with the model. First, the agent sets up the client passing the `base URL` for NEAR AI and an `API key` for the Cloud (we'll explain how to obtain a key in the next section). ``` const openai = new OpenAI({ baseURL: 'https://cloud-api.near.ai/v1', apiKey: process.env.NEAR_AI_API_KEY, }); ``` A request to an LLM typically takes two prompts: - **The System Message** sets the `behavior and role` of the LLM. In this tutorial, the message explains to the model that it's acting as a DAO and needs to vote Approved or Rejected on proposals, making its decisions based on the manifesto. - **The User Message** is the `input` that the LLM responds to. In typical chat applications, this would be any message you type to the assistant. In this tutorial, the user message is a combination of the proposal and the DAO's manifesto. ``` // Set the system message that will be sent to the AI model const systemMessage = "You are a Decentralized Autonomous Organization (DAO) agent. You are responsible for making decisions on behalf of the DAO. Each prompt will contain the manifesto you use to vote and a proposal that you will vote on. You will vote on the proposal based on the manifesto. You will provide both your vote (Approved or Rejected) and a clear explanation of your reasoning based on how the proposal aligns with the manifesto. You must keep responses under 10,000 characters."; // Create the user message that will be sent to the AI model, a combination of the manifesto and the proposal const userMessage = ` Manifesto: ${manifesto} Proposal: ${proposal} `; ``` Next, the agent constructs the `JSON request` to send to the model. There are several important aspects of this request: - The request specifies the `model` to call - in this tutorial, DeepSeek V3 0324. You can find a full list of [available models here](https://cloud.near.ai/models). - The request is using `non-streaming` mode. This means the model waits until the full response is ready before returning it, rather than streaming, where the response is sent piece by piece while the model is still generating it. Non-streaming is simpler here as the agent doesn't need to display the response or take any action until the whole response is ready. - The request uses `tool calling` to ensure that the model responds with a vote of exactly `Approved` or `Rejected` and `reasoning` for its choice. If the model fails to conform to the required output, it will return an error. You can read more on [tool calling/function calling here](https://platform.openai.com/docs/guides/function-calling). ``` const request: ChatCompletionCreateParamsNonStreaming = { model: "deepseek-ai/DeepSeek-V3.1", tools: [ { type: "function", function: { name: "dao_vote", description: "Vote on a DAO proposal with reasoning", parameters: { type: "object", properties: { vote: { type: "string", enum: ["Approved", "Rejected"] }, reasoning: { type: "string", description: "Explanation for the voting decision based on the manifesto" } }, required: ["vote", "reasoning"] } } } ], tool_choice: { type: "function", function: { name: "dao_vote" } }, messages: [ { role: "system", content: systemMessage }, { role: "user", content: userMessage } ] }; ``` The agent then sends the request to the model, extracts the vote and reasoning from the response, and performs a double check to ensure the response is in the expected format. ``` // Send the request to the AI model const completion = await openai.chat.completions.create(request); // Extract the information from the response const toolCall = completion.choices[0]?.message?.tool_calls?.[0]; if (!toolCall || toolCall.type !== 'function') { throw new Error('Expected function tool call response'); } const rawResponse = JSON.parse(toolCall.function.arguments); // Validate the vote is exactly "Approved" or "Rejected" if (rawResponse.vote !== "Approved" && rawResponse.vote !== "Rejected") { throw new Error(`Invalid vote: "${rawResponse.vote}". Vote must be exactly "Approved" or "Rejected"`); } // Check that reasoning is under 10,000 characters if (rawResponse.reasoning.length > 10000) { throw new Error(`AI response too long: ${rawResponse.reasoning.length} characters. Must be under 10,000 characters.`); } ``` --- ## Submitting the Vote Once the agent receives the response from the LLM, it's nearly ready to submit the vote to the agent contract. Before sending the vote, the agent `hashes` both the proposal it's voting on and the manifesto it's using to make it's decision. This is done so the DAO can verify that the agent used the correct proposal and manifesto to vote and that the query wasn't corrupted by the RPC or intercepted and modified on transit to the agent. ``` const proposal_hash: string = crypto.createHash('sha256').update(proposal_text).digest('hex'); const manifesto_hash: string = crypto.createHash('sha256').update(manifesto_text).digest('hex'); ``` The agent then calls `agent_vote` on the agent contract using the `agentCall` function provided by `shade-agent-js` to cast its vote. It includes the yield ID of the proposal's yielded promise that it's resuming, along with all required response fields. ``` const response = { proposal_id: proposal_id, proposal_hash: proposal_hash, manifesto_hash: manifesto_hash, vote: voteResult.vote, reasoning: voteResult.reasoning }; await agentCall({ methodName: "agent_vote", args: { yield_id: yield_id, response: response }, }); ``` --- ## Next Steps That completes the overview of the DAO system as a whole! You can now fork the [repository](https://github.com/NearDeFi/verifiable-ai-dao/tree/main) to create your own yield and resume-based Shade Agent. On the [final page](./deploying.md) of this tutorial, you'll learn how to deploy the AI DAO yourself. --- # Source: https://docs.near.org/primitives/dao.md --- id: dao title: Decentralized Autonomous Organizations sidebar_label: Autonomous Organizations (DAO) description: "Learn about Decentralized Autonomous Organizations (DAOs) on NEAR - self-organized groups that coordinate membership, decision-making, and funding through smart contract voting." --- Decentralized Autonomous Organizations (DAOs) are self-organized groups that form around common purposes. Membership, decision-making, and funding are coordinated by publicly voting on proposals through a smart contract. ![dao](/assets/docs/primitives/dao.png) In contrast with [FT](./ft/ft.md) and [NFT](./nft/nft.md), DAO contract's are not standardized. Because of this, on this page we will use as reference the [sputnik dao contract](https://github.com/near-daos/sputnik-dao-contract). The main concepts covered here should easily generalizable to other DAO implementations. :::tip The simplest way to create and interact with a DAO is to go through the [AstraDAO UI](https://near.social/astraplusplus.ndctools.near/widget/home?page=daos). ::: --- ## Create a DAO You can create a DAO by interacting with the `sputnik-dao` contract: ```js import { useNearWallet } from "near-connect-hooks"; const DAO_FACTORY_CONTRACT_ADDRESS = 'sputnik-dao.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: DAO_FACTORY_CONTRACT_ADDRESS, method: 'create', args: { name: 'primitives', args: btoa({ config: { name: 'Primitives', purpose: 'Building primitives on NEAR', metadata: '', }, policy: ['bob.near'], }), }, gas: 300000000000000, deposit: 6000000000000000000000000, }); ``` :::note The full list of roles and permissions you can find [here](https://github.com/near-daos/sputnik-dao-contract#roles-and-permissions). ::: Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application ```bash export COUNCIL='["bob.near"]' export ARGS=`echo '{"config": {"name": "Primitives", "purpose": "Building primitives on NEAR", "metadata":""}, "policy": '$COUNCIL'}' | base64` near call sputnikv2.testnet create "{\"name\": \"primitives\", \"args\": \"$ARGS\"}" --useAccount bob.near --amount 6 --gas 150000000000000 ``` :::note The full list of roles and permissions you can find [here](https://github.com/near-daos/sputnik-dao-contract#roles-and-permissions). ::: }> :::note You can find the complete list of [roles and permissions here](https://github.com/near-daos/sputnik-dao-contract#roles-and-permissions). ::: ```rust // Validator interface, for cross-contract calls #[ext_contract(ext_dao_factory_contract)] trait ExternalDaoFactoryContract { fn create(&mut self, name: AccountId, args: Base64VecU8) -> Promise; } // Implement the contract structure #[near] impl Contract { #[payable] pub fn create_dao(&mut self, name: AccountId, args: Base64VecU8) -> Promise { let promise = ext_dao_factory_contract::ext(self.dao_factory_contract.clone()) .with_attached_deposit(env::attached_deposit()) .with_static_gas(Gas(30*TGAS)) .create(name, args); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(Gas(50*TGAS)) .external_common_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn external_common_callback(&self, #[callback_result] call_result: Result<(), PromiseError>) { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting external contract") } } } ``` :::tip The simplest way to create and interact with a DAO is to go through the [AstraDAO UI](https://near.social/astraplusplus.ndctools.near/widget/home?page=daos). :::
### Using Global Contract You can deploy a new DAO using our global contract - a pre-deployed [a Sputnik DAO contract](https://github.com/near-daos/sputnik-dao-contract/tree/main/sputnikdao2) that you can reuse. [Global contracts](../smart-contracts/global-contracts.md) are deployed once and can be reused by any account without incurring high storage costs. ```bash near contract deploy use-global-account-id dao.globals.primitives.testnet \ with-init-call new \ json-args '{"config": {"name": "Primitives", "purpose": "Building primitives on NEAR", "metadata":""}, "policy": [""]}' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain \ send ``` ```bash near contract deploy use-global-hash Ea8tHXFSQVszVwGASyzAfLq65DjcRDhkfab4FcPaRpgD \ with-init-call new \ json-args '{"config": {"name": "Primitives", "purpose": "Building primitives on NEAR", "metadata":""}, "policy": [""]}' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain \ send ``` :::note Deploying by **hash** creates an immutable contract that never changes. Deploying by **account ID** creates an updatable contract that changes when the referenced account's contract is updated. Choose based on whether you want your FT contract to be updatable or permanent. :::
### Voting policy Currently, DAOs support two different types of [voting policies](https://github.com/near-daos/sputnik-dao-contract#voting-policy): `TokenWeight`, and `RoleWeight`. When the vote policy is `TokenWeight`, the council votes using [tokens](./ft/ft.md). The weigh of a vote is the proportion of tokens used for voting over the token's total supply. When the vote policy is `RoleWeight(role)`, the vote weigh is computed as "one over the total number of people with the role".
Voting Threshold Both voting policies further include a `threshold` for passing a proposal, which can be a ratio or a fixed number. The ratio indicates that you need a proportion of people/tokens to approve the proposal (e.g. half the people need to vote, and to vote positively). A fixed number indicated that you need a specific number of votes/tokens to pass the proposal (e.g. 3 people/tokens are enough to approve the proposal).
--- ## List of DAOs Query the list of DAOs existing in Sputnik Dao. ```js import { useNearWallet } from "near-connect-hooks"; const DAO_FACTORY_CONTRACT_ADDRESS = 'sputnik-dao.near'; const { viewFunction } = useNearWallet(); await viewFunction({ method: 'get_dao_list', args: {}, contractId: DAO_FACTORY_CONTRACT_ADDRESS, }); ``` Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application ```bash near view sputnik-dao.near get_dao_list '{}' ```
Example response

```bash [ 'ref-finance.sputnik-dao.near' 'gaming-dao.sputnik-dao.near', ... ] ```

}>
Example response ```bash [ 'ref-finance.sputnik-dao.near' 'gaming-dao.sputnik-dao.near', ... ] ```
--- ## Query Existing Proposals These snippets will enable you to query the proposals existing in a particular DAO. ```js import { useNearWallet } from "near-connect-hooks"; const DAO_CONTRACT_ADDRESS = 'nearweek-news-contribution.sputnik-dao.near'; const { viewFunction } = useNearWallet(); await viewFunction({ method: 'get_proposals', args: { from_index: 9262, limit: 2 }, contractId: DAO_CONTRACT_ADDRESS, }); ``` Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application ```bash near view nearweek-news-contribution.sputnik-dao.near get_proposals '{"from_index": 9262, "limit": 2}' ```
Example response

```bash [ { id: 9262, proposer: 'pasternag.near', description: 'NEAR, a top non-EVM blockchain, has gone live on Router’s Testnet Mandara. With Router Nitro, our flagship dApp, users in the NEAR ecosystem can now transfer test tokens to and from NEAR onto other supported chains. $$$$https://twitter.com/routerprotocol/status/1727732303491961232', kind: { Transfer: { token_id: '', receiver_id: 'pasternag.near', amount: '500000000000000000000000', msg: null } }, status: 'Approved', vote_counts: { council: [ 1, 0, 0 ] }, votes: { 'brzk-93444.near': 'Approve' }, submission_time: '1700828277659425683' }, { id: 9263, proposer: 'fittedn.near', description: 'How to deploy BOS component$$$$https://twitter.com/BitkubAcademy/status/1728003163318563025?t=PiN6pwS380T1N4JuQXSONA&s=19', kind: { Transfer: { token_id: '', receiver_id: 'fittedn.near', amount: '500000000000000000000000', msg: null } }, status: 'InProgress', vote_counts: { 'Whitelisted Members': [ 1, 0, 0 ] }, votes: { 'trendheo.near': 'Approve' }, submission_time: '1700832601849419123' } ] ```

}>
Example response ```bash [ { "id": 9262, "proposer": "pasternag.near", "description": "NEAR, a top non-EVM blockchain, has gone live on Router’s Testnet Mandara. With Router Nitro, our flagship dApp, users in the NEAR ecosystem can now transfer test tokens to and from NEAR onto other supported chains. $$$$https://twitter.com/routerprotocol/status/1727732303491961232", "kind": { "Transfer": { "token_id": "", "receiver_id": "pasternag.near", "amount": "500000000000000000000000", "msg": null } }, "status": "Approved", "vote_counts": { "council": [1, 0, 0] }, "votes": { "brzk-93444.near": "Approve" }, "submission_time": "1700828277659425683" }, { "id": 9263, "proposer": "fittedn.near", "description": "How to deploy BOS component$$$$https://twitter.com/BitkubAcademy/status/1728003163318563025?t=PiN6pwS380T1N4JuQXSONA&s=19", "kind": { "Transfer": { "token_id": "", "receiver_id": "fittedn.near", "amount": "500000000000000000000000", "msg": null } }, "status": "Expired", "vote_counts": { "Whitelisted Members": [2, 0, 0] }, "votes": { "trendheo.near": "Approve", "vikash.near": "Approve" }, "submission_time": "1700832601849419123" } ] ```
--- ## Create proposal Create a proposal so other users can vote in favor or against it. ```js import { useNearWallet } from "near-connect-hooks"; const DAO_CONTRACT_ADDRESS = 'primitives.sputnik-dao.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: DAO_CONTRACT_ADDRESS, method: 'add_proposal', args: { proposal: { description: 'My first proposal', kind: { Transfer: { token_id: '', receiver_id: 'bob.near', amount: '10000000000000000000000000', }, }, }, }, gas: 300000000000000, deposit: 100000000000000000000000, }); ``` Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application ```bash near call primitives.sputnik-dao.near add_proposal '{"proposal": {"description": "My first proposal", "kind": { "Transfer": {"token_id": "", "receiver_id": "bob.near", "amount": "10000000000000000000000000"}}}}' --deposit 0.1 --gas 300000000000000 --useAccount bob.near ``` }> ```rust // Account ID that represents a token in near-sdk v3 // Need to keep it around for backward compatibility pub type OldAccountId = String; // How the voting policy votes get weighted. #[near(serializers = [json, borsh]) #[derive(Clone, PartialEq)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] pub enum WeightKind { // Using token amounts and total delegated at the moment. TokenWeight, // Weight of the group role. Roles that don't have scoped group are not supported. RoleWeight, } // Direct weight or ratio to total weight, used for the voting policy #[near(serializers = [json, borsh]) #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] #[serde(untagged)] pub enum WeightOrRatio { Weight(U128), Ratio(u64, u64), } // Defines configuration of the vote #[near(serializers = [json, borsh]) #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] pub struct VotePolicy { // Kind of weight to use for votes. pub weight_kind: WeightKind, // Minimum number required for vote to finalize. // If weight kind is TokenWeight - this is minimum number of tokens required. // This allows to avoid situation where the number of staked tokens from total supply is too small. // If RoleWeight - this is minimum number of votes. // This allows to avoid situation where the role is got too small but policy kept at 1/2, for example. pub quorum: U128, // How many votes to pass this vote. pub threshold: WeightOrRatio, } #[near(serializers = [json, borsh])] #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] pub enum RoleKind { // Matches everyone, who is not matched by other roles. Everyone, // Member greater or equal than given balance. Can use `1` as non-zero balance. Member(U128), // Set of accounts. Group(HashSet), } #[near(serializers = [json, borsh])] #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] pub struct RolePermission { // Name of the role to display to the user. pub name: String, // Kind of the role: defines which users this permissions apply. pub kind: RoleKind, // Set of actions on which proposals that this role is allowed to execute. // : pub permissions: HashSet, // For each proposal kind, defines voting policy. pub vote_policy: HashMap, } // Defines voting / decision making policy of this DAO #[near(serializers = [json, borsh])] #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] pub struct Policy { // List of roles and permissions for them in the current policy. pub roles: Vec, // Default vote policy. Used when given proposal kind doesn't have special policy. pub default_vote_policy: VotePolicy, // Proposal bond. pub proposal_bond: U128, // Expiration period for proposals. pub proposal_period: U64, // Bond for claiming a bounty. pub bounty_bond: U128, // Period in which giving up on bounty is not punished. pub bounty_forgiveness_period: U64, } // Versioned policy #[near(serializers = [json, borsh])] #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug, PartialEq))] pub enum VersionedPolicy { // Default policy with given accounts as council. Default(Vec), Current(Policy), } // Function call arguments #[near(serializers = [json, borsh])] #[cfg_attr(not(target_arch = "wasm32"), derive(Clone, Debug))] pub struct ActionCall { method_name: String, args: Base64VecU8, deposit: U128, gas: U64, } // Bounty information. #[near(serializers = [json, borsh])] #[derive(Clone)] #[cfg_attr(not(target_arch = "wasm32"), derive(Debug))] pub struct Bounty { /// Description of the bounty. pub description: String, /// Token the bounty will be paid out. /// Can be "" for $NEAR or a valid account id. pub token: OldAccountId, /// Amount to be paid out. pub amount: U128, /// How many times this bounty can be done. pub times: u32, /// Max deadline from claim that can be spend on this bounty. pub max_deadline: U64, } // Info about factory that deployed this contract and if auto-update is allowed #[near(serializers = [json, borsh])] #[cfg_attr(not(target_arch = "wasm32"), derive(Clone, Debug))] pub struct FactoryInfo { pub factory_id: AccountId, pub auto_update: bool, } // Function call arguments #[near(serializers = [json, borsh])] #[cfg_attr(not(target_arch = "wasm32"), derive(Clone, Debug))] pub struct PolicyParameters { pub proposal_bond: Option, pub proposal_period: Option, pub bounty_bond: Option, pub bounty_forgiveness_period: Option, } // Votes recorded in the proposal #[near(serializers = [json, borsh])] #[derive(Clone, Debug)] pub enum Vote { Approve = 0x0, Reject = 0x1, Remove = 0x2, } // Configuration of the DAO #[near(serializers = [json, borsh])] #[derive(Clone, Debug)] pub struct Config { // Name of the DAO. pub name: String, // Purpose of this DAO. pub purpose: String, // Generic metadata. Can be used by specific UI to store additional data. // This is not used by anything in the contract. pub metadata: Base64VecU8, } // Kinds of proposals, doing different action #[near(serializers = [json, borsh])] #[cfg_attr(not(target_arch = "wasm32"), derive(Clone, Debug))] pub enum ProposalKind { // Change the DAO config. ChangeConfig { config: Config }, // Change the full policy. ChangePolicy { policy: VersionedPolicy }, // Add member to given role in the policy. This is short cut to updating the whole policy. AddMemberToRole { member_id: AccountId, role: String }, // Remove member to given role in the policy. This is short cut to updating the whole policy. RemoveMemberFromRole { member_id: AccountId, role: String }, // Calls `receiver_id` with list of method names in a single promise. // Allows this contract to execute any arbitrary set of actions in other contracts. FunctionCall { receiver_id: AccountId, actions: Vec, }, // Upgrade this contract with given hash from blob store. UpgradeSelf { hash: Base58CryptoHash }, // Upgrade another contract, by calling method with the code from given hash from blob store. UpgradeRemote { receiver_id: AccountId, method_name: String, hash: Base58CryptoHash, }, // Transfers given amount of `token_id` from this DAO to `receiver_id`. // If `msg` is not None, calls `ft_transfer_call` with given `msg`. Fails if this base token. // For `ft_transfer` and `ft_transfer_call` `memo` is the `description` of the proposal. Transfer { // Can be "" for $NEAR or a valid account id. token_id: OldAccountId, receiver_id: AccountId, amount: U128, msg: Option, }, // Sets staking contract. Can only be proposed if staking contract is not set yet. SetStakingContract { staking_id: AccountId }, // Add new bounty. AddBounty { bounty: Bounty }, // Indicates that given bounty is done by given user. BountyDone { bounty_id: u64, receiver_id: AccountId, }, // Just a signaling vote, with no execution. Vote, // Change information about factory and auto update. FactoryInfoUpdate { factory_info: FactoryInfo }, // Add new role to the policy. If the role already exists, update it. This is short cut to updating the whole policy. ChangePolicyAddOrUpdateRole { role: RolePermission }, // Remove role from the policy. This is short cut to updating the whole policy. ChangePolicyRemoveRole { role: String }, // Update the default vote policy from the policy. This is short cut to updating the whole policy. ChangePolicyUpdateDefaultVotePolicy { vote_policy: VotePolicy }, // Update the parameters from the policy. This is short cut to updating the whole policy. ChangePolicyUpdateParameters { parameters: PolicyParameters }, } #[near(serializers = [json])] pub struct ProposalInput { /// Description of this proposal. pub description: String, /// Kind of proposal with relevant information. pub kind: ProposalKind, } // Validator interface, for cross-contract calls #[ext_contract(ext_dao_contract)] trait ExternalDaoContract { fn add_proposal(&mut self, proposal: ProposalInput) -> Promise; } // Implement the contract structure #[near] impl Contract { #[payable] pub fn create_proposal(&mut self, proposal: ProposalInput) -> Promise { let promise = ext_dao_contract::ext(self.dao_contract.clone()) .with_attached_deposit(env::attached_deposit()) .with_static_gas(Gas(5*TGAS)) .add_proposal(proposal); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(Gas(50*TGAS)) .external_proposal_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn external_proposal_callback(&self, #[callback_result] call_result: Result) -> Option { if call_result.is_err() { log!("There was an error contacting external contract"); return None; } // Return the proposal id let id = call_result.unwrap(); return Some(id); } } ``` :::info By default, only **council members** can create proposals. ::: --- ## Vote for proposal These snippet will enable your users to cast a vote for proposal of a particular DAO. ```js import { useNearWallet } from "near-connect-hooks"; const DAO_CONTRACT_ADDRESS = 'primitives.sputnik-dao.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: DAO_CONTRACT_ADDRESS, method: 'act_proposal', args: { id: 0, action: 'VoteApprove' }, gas: 300000000000000, }); ``` :::note Available vote options: `VoteApprove`, `VoteReject`, `VoteRemove`. ::: Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application ```bash near call primitives.sputnik-dao.near act_proposal '{"id": 0, "action": "VoteApprove"}' --gas 300000000000000 --useAccount bob.near ``` :::note Available vote options: `VoteApprove`, `VoteReject`, `VoteRemove`. ::: }> :::note Available vote options: `VoteApprove`, `VoteReject`, `VoteRemove`. ::: ```rust // Set of possible action to take #[near(serializers = [json, borsh])] #[derive(Debug)] pub enum Action { // Action to add proposal. Used internally. AddProposal, // Action to remove given proposal. Used for immediate deletion in special cases. RemoveProposal, // Vote to approve given proposal or bounty. VoteApprove, // Vote to reject given proposal or bounty. VoteReject, // Vote to remove given proposal or bounty (because it's spam). VoteRemove, // Finalize proposal, called when it's expired to return the funds // (or in the future can be used for early proposal closure). Finalize, // Move a proposal to the hub to shift into another DAO. MoveToHub, } // Validator interface, for cross-contract calls #[ext_contract(ext_dao_contract)] trait ExternalDaoContract { fn act_proposal(&mut self, id: u64, action: Action, memo: Option) -> Promise; } // Implement the contract structure #[near] impl Contract { #[payable] pub fn act_proposal(&mut self, id: u64, action: Action, memo: Option) -> Promise { let promise = ext_dao_contract::ext(self.dao_contract.clone()) .with_attached_deposit(env::attached_deposit()) .with_static_gas(Gas(10*TGAS)) .act_proposal(id, action, memo); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .external_common_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn external_common_callback(&self, #[callback_result] call_result: Result<(), PromiseError>) { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting external contract") } } } ``` --- ## Additional Resources 1. [NEAR Treasury](https://neartreasury.com/) - a Treasury management web app built on top of the Sputnik DAO Contract. Allows users to create and manage treasury funds with ease. --- # Source: https://docs.near.org/data-infrastructure/data-apis.md --- id: data-apis title: Data APIs description: "Explore community-built APIs for accessing on-chain data" --- If you are building a decentralized applications, chances are that you will need to query on-chain data. Since building a full indexer is not always feasible, the community has created a set of APIs that you can use to query data from the NEAR blockchain. These APIs provide a simple way to access on-chain data without having to run your own indexer or node. They are designed to be easy to use and provide a wide range of functionality, from querying account balances to exploring transactions and blocks. --- ## FastNEAR API The [FastNEAR API](https://github.com/fastnear/fastnear-api-server-rs?tab=readme-ov-file#api-v1) allows to easily query the NEAR blockchain to get an account's assets, map keys into account IDs, explore a block's transactions, etc. Possible use cases include: - Querying all assets of an account (including fungible and non-fungible tokens) - Querying the last block produced - Mapping Public Key to Account ID - Mapping Full Access Public Key to Account ID - Knowing a user's staking pools (validators) - Querying the top holders of a token
#### Examples ```bash # Query user's FTs curl https://api.fastnear.com/v1/account/root.near/ft # Query user's NFTs curl https://api.fastnear.com/v1/account/root.near/nft # Query all user's assets curl https://api.fastnear.com/v1/account/root.near/full ``` --- ## NearBlocks API [NearBlocks API](https://api.nearblocks.io/api-docs/) provides an endpoint to query actions that happened on a NEAR account, possible use cases include: - Query an account balance - Query all function calls to specific contract - Get total NEAR supply and circulating supply - Query the number of total transactions on NEAR
#### Examples ```bash # All the transactions where somebody called `create_drop` on Keypom curl -X GET "https://api.nearblocks.io/v1/account/v2.keypom.near/txns?method=create_drop" # All the times that `gagdiez.near` called `create_drop` on Keypom curl -X GET "https://api.nearblocks.io/v1/account/v2.keypom.near/txns?method=create_drop&from=gagdiez.near" ``` --- ## Pikespeak API The [Pikespeak API](https://doc.pikespeak.ai/) allows you to fetch blockchain events and aggregated analytics on wallets, validators, delegators, money transfers, dApps activity, and more. Use case includes: - Querying account balances - Querying the most active wallets - Querying historic account events _To access the Pikespeak API you'll need to [register and create an account](https://pikespeak.ai/plans). Once you're registered, under the [`My Account`](https://pikespeak.ai/myaccount) page you can get your API key_
#### Examples ```sh # Check the account balance for `root.near`: curl -X GET https://api.pikespeak.ai/account/balance/root.near -H "accept: application/json" -H "x-api-key: YOUR-PIKESPEAK-API-KEY" # Most active wallets NEAR senders curl -X GET https://api.pikespeak.ai/hot-wallets/near -H "accept: application/json" -H "x-api-key: YOUR-PIKESPEAK-API-KEY" # Get historic account events for `keypom.near` curl -X GET https://api.pikespeak.ai/event-historic/keypom.near -H "accept: application/json" -H "x-api-key: YOUR-PIKESPEAK-API-KEY" ``` --- ## The Graph [The Graph](https://thegraph.com/docs/en/cookbook/near/) gives developers tools to process blockchain events and make the resulting data easily available via a GraphQL API, known individually as a subgraph. [Graph Node](https://github.com/graphprotocol/graph-node) is now able to process NEAR events, which means that NEAR developers can now build subgraphs to index their smart contracts. --- ## SubQuery [SubQuery](https://academy.subquery.network/quickstart/quickstart_chains/near.html): A fast, flexible, and reliable open-source data indexer that provides you with custom APIs for your web3 project across NEAR and many other chains --- # Source: https://docs.near.org/chain-abstraction/data-availability.md --- id: data-availability title: Rollup Data Availability description: "Learn about NEAR's Data Availability layer for rollups, including blob store contracts, light clients, RPC nodes, and integrations with L2 solutions like Polygon CDK and Optimism." --- Every monolithic blockchain has a data availability layer. NEAR's Data Availability (DA) represents a pioneering initiative to modularize the data availability layer from the NEAR blockchain to make it available as a roll-up solution for builders on other chains. This infrastructure consists of a smart contract, a light client, and a Remote Procedure Call (RPC) node. The smart contract is designed to accept blob data, which is then processed through NEAR's consensus. The RPC node functions as the serving node, where users can transmit their data. Lastly, the light client operates as a node that rollups can verify to ensure the availability of data. - [Blob Store Contract](#blob-store-contract): A contract that provides the store for arbitrary DA blobs. - [Light Client](#light-client): A trustless off-chain light client for NEAR with DA-enabled features. - [RPC Client](#da-rpc): The defacto client for submitting data blobs to NEAR. - [Integrations](#integrations): Proof of concept works for integrating with L2 rollups. NEAR DA is notably inexpensive due to several key factors: - NEAR offers a substantial amount of block space per shard, ensuring efficient utilization. - NEAR optimizes this space by avoiding unnecessary cryptographic bloat, ensuring that each 4MB allocated equals precisely 4MB of usable data. - NEAR's scalability is unmatched, as it can readily reshard and scale in response to increasing demand, unlike competitors who would need to resort to constructing rollups or sidechains, thus maintaining a consistently ample and cost-effective data availability solution. :::tip For the latest information, please check the [Near DA](https://github.com/near/rollup-data-availability/) repository. ::: --- ## System Context This outlines the system components that we build and how it interacts with external components. Red lines denote external flow of commitments. White lines denote flow of blob data. :::note `Fisherman` is just an example how a rollup can work with the light client in the initial stage of DA, until we implement a more non-interactive approach, such as KZG. ::: ```mermaid C4Context title NEAR Data Availability System Context Enterprise_Boundary(b1, "Ethereum") { System_Ext(SystemEth, "Ethereum") System_Boundary(b2, "Rollup") { System_Ext(SystemRollup, "Rollup", "Derives blocks, execute transactions, posts commitments & sequence data") System(SystemNearDa, "NEAR DA Client", "Submits/Gets blob data, creates commitments") } BiRel(SystemRollup, SystemEth, "Posts sequences, proofs of execution, DA frame references") BiRel(SystemRollup, SystemNearDa, "Post batches, retrieves commitments") Rel(fisherman, SystemEth, "Looks for commitments, posts results") } Enterprise_Boundary(b0, "NEAR") { System(SystemLc, "Light Client", "Syncs headers, provides inclusion proofs") System(SystemNear, "NEAR Protocol", "NEAR validators, archival nodes") Rel(SystemLc, SystemNear, "Syncs headers") Rel(SystemNearDa, SystemNear, "Submits/Gets blob") %% This doesn't exist yet %% System(SystemDas, "Data Availability Sampling", "Data redundancy, retrieval, sample responses") %% BiRel(SystemDas, SystemLc, "Commitments") } Person_Ext(fisherman, "Fisherman") Rel(fisherman, SystemLc, "Requests inclusion proofs, validates inclusion proofs") UpdateRelStyle(fisherman, SystemEth, $offsetY="-10" $lineColor="red") UpdateRelStyle(fisherman, SystemLc, $offsetY="-10", $lineColor="red") UpdateRelStyle(SystemRollup, SystemEth, $offsetY="-30", $lineColor="white") UpdateElementStyle(fisherman, $bgColor="grey", $borderColor="red") UpdateRelStyle(SystemRollup, SystemNearDa, $offsetX="-200", $lineColor="white", $textColor="white") UpdateRelStyle(SystemNearDa, SystemNear, $textColor="white", $lineColor="white", $offsetY="10") UpdateRelStyle(SystemNearLc, SystemNear, $offsetX="30") ``` --- ## [Blob Store Contract](https://github.com/near/rollup-data-availability/tree/main/contracts/blob-store) The [blob store contract](https://github.com/near/rollup-data-availability/tree/main/contracts/blob-store) provides the store for arbitrary DA blobs. In practice, these "blobs" are sequencing data from rollups, but they can be any data. NEAR blockchain state storage is pretty cheap. At the time of writing, 100KiB is a flat fee of 1NEAR. To limit the costs of NEAR storage even more, we don't store the blob data in the blockchain state. It works by taking advantage of NEAR consensus around receipts. When a chunk producer processes a receipt, there is consensus around the receipt. However, once the chunk has been processed and included in the block, the receipt is no longer required for consensus and can be pruned. The pruning time is at least 3 NEAR epochs, where each epoch is 12 Hours; in practice, this is around five epochs. Once the receipt has been pruned, it is the responsibility of archival nodes to retain the transaction data, and we can even get the data from indexers. We can validate that the blob was retrieved from ecosystem actors in the format submitted by checking the blob commitment. The blob commitment currently needs to be more efficient and will be improved, but it benefits us because anybody can build this with limited expertise and tooling. It is created by taking a blob, chunking it into 256-byte pieces, and creating a Merkle tree, where each leaf is a Sha-256 hash of the shard. The root of the Merkle tree is the blob commitment, which is provided as [transaction_id ++ commitment] to the L1 contract, which is 64 bytes. What this means: - Consensus is provided around the submission of a blob by NEAR validators - The function input data is stored by full nodes for at least three days - Archival nodes can store the data for longer - We don't occupy consensus with more data than needs to be - Indexers can also be used, and this Data is currently indexed by all significant explorers in NEAR - The commitment is available for a long time, and the commitment is straightforward to create --- ## [Light Client](https://github.com/near/rollup-data-availability/tree/main/) A trustless off-chain light client for NEAR with DA-enabled features, Such as KZG commitments, Reed-Solomon erasure coding & storage connectors. The light client provides easy access to transaction and receipt inclusion proofs within a block or chunk. This is useful for checking any dubious blobs which may not have been submitted or validating that a blob has been submitted to NEAR. A blob submission can be verified by: - Taking the NEAR transaction ID from Ethereum for the blob commitment. - Ask the light client for an inclusion proof for the transaction ID or the receipt ID if you're feeling specific; this will give you a Merkle inclusion proof for the transaction/receipt. - Once you have the inclusion proof, you can ask the light client to verify the proof for you, or advanced users can manually verify it themselves. - Armed with this knowledge, rollup providers can have advanced integration with light clients and build proving systems around it. In the future, we will provide extensions to light clients such that non-interactive proofs can be supplied for blob commitments and other data availability features. It's also possible that the light client may be on-chain for the header syncing and inclusion proof verification, but this is a low priority right now. --- ## DA RPC This client is the defacto client for submitting blobs to NEAR. These crates allow a client to interact with the blob store. It can be treated as a "black box", where blobs go in, and `[transaction_id ++ commitment]` emerges. There are multiple versions: - The [`da-rpc` crate](https://github.com/near/rollup-data-availability/tree/main/crates/da-rpc) is the rust client, which anyone can use if they prefer rust in their application. The responsibility of this client is to provide a simple interface for interacting with NEAR DA. - The [`da-rpc-sys` crate](https://github.com/near/rollup-data-availability/tree/main/crates/da-rpc-sys) is the FFI client binding for use by non-rust applications. This calls through to `da-rpc` to interact with the blob store, with some additional black box functionality for dealing with pointers wrangling and such. - The [`da-rpc-go` package](https://github.com/near/rollup-data-availability/tree/main/gopkg/da-rpc) is the go client bindings for use by non-rust applications, and this calls through to `da-rpc-sys`, which provides another application-level layer for easy interaction with the bindings. :::info See also [the diagram](https://github.com/near/near-cli-rs/blob/main/docs/da_rpc_client.md) ::: --- ## Integrations We have developed some proof of concept works for integrating with L2 rollups: - [CDK Stack](https://github.com/firatNEAR/cdk-validium-node/tree/near): We have integrated with the Polygon CDK stack. Using the Sequence Sender for submissions to NEAR. - [Optimism](https://github.com/near/optimism): We have integrated with the Optimism OP stack. Using the `Batcher` for submissions to NEAR and the proposer for submitting NEAR commitment data to Ethereum. - [Arbitrum Nitro](https://github.com/near/nitro): We have integrated a small plugin into the DAC daserver. This is much like our http sidecar and provides a very modular integration into NEAR DA whilst supporting arbitrum DACs. :::info In the future, the `Arbitrum Nitro` integration will likely be the easiest way to support NEAR DA as it acts as an independent sidecar which can be scaled as needed. This also means that the DAC can opt-in and out of NEAR DA, lowering their infrastructure burden. With this approach, the DAC committee members just need to have a "dumb" signing service, with the store backed by NEAR. ::: --- # Source: https://docs.near.org/data-infrastructure/data-services.md --- id: data-services title: Existing Services description: "Indexers are constantly listening for transactions and storing them so they can be easily queried." --- Data Services are constantly listening to the blockchain, processing the transactions and storing them in a database that can be easily queried. You can use them to access blockchain data efficiently: - [BigQuery](./big-query.md): Blockchain data indexing in NEAR Public Lakehouse is for anyone wanting to understand blockchain data. - [NEAR Lake Framework](./near-lake-framework.md): a companion library to NEAR Lake. It allows you to build your own indexer that watches a stream of blocks **from a NEAR Lake data source** and allows you to **create your own logic to process that data**. Keep in mind this is **the one you want to use for future projects**, instead of the Indexer Framework. Read [why it is better](/data-infrastructure/near-lake-framework#comparison-with-near-indexer-framework). - [Indexer.xyz Multichain Indexer](https://indexer.xyz/): Indexer.xyz is an application layer that you can build your NFT or DeFi applications entirely on top of. In addition to raw transaction indexing, Indexer.xyz provides you with a standardized GraphQL API layer to easily tap into transactions across contracts and chains. - [The Graph](https://thegraph.com/docs/en/cookbook/near/): development tools to process blockchain events and make the resulting data easily available via a GraphQL API, known individually as a subgraph. [Graph Node](https://github.com/graphprotocol/graph-node) is able to process NEAR events, which means that NEAR developers can build subgraphs to index their smart contracts. - [GetBlock](https://getblock.io/explorers/near/blocks/): developer tools offering a simple and reliable API access to multiple blockchains including NEAR Protocol. - [Community APIs](./data-api.md): build precise & reliable dApps with our community's APIs. - [Covalent](https://www.covalenthq.com/docs/networks/aurora/): for [Aurora EVM](https://aurora.dev/) indexing, Covalent provides a unified API bringing visibility to billions of Web3 data points. - [NEAR Indexer Framework](https://github.com/near/nearcore/tree/master/chain/indexer): a micro-framework providing you with a "live" stream of blocks. Useful to handle on-chain real-time `events`. - [SubQuery](https://academy.subquery.network/quickstart/quickstart_chains/near.html): is an end to end multi-blockchain indexing solution that provides NEAR developers with fast, flexible, universal, open source and decentralized APIs for web3 projects. The [NEAR starter project](https://github.com/subquery/near-subql-starter/tree/main/Near/near-starter) provides a template for developers to get up and running within minutes. --- # Source: https://docs.near.org/web3-apps/concepts/data-types.md --- id: data-types title: Handling NEAR Types description: "Learn how to handle common data types when interacting with NEAR" --- When calling methods in a contract or receiving results from them, you will need to encode/decode parameters correctly. For this, it is important to know how the contracts encode timestamps, balances, and gas. :::info Searching for Smart Contract Data Types? Check the [Smart Contract Data Types](../../smart-contracts/anatomy/types.md) section for more information on how to handle data types within smart contracts. ::: --- ## Time The block timestamp in a smart contract is encoded using nanoseconds (i.e. 19 digits: `1655373910837593990`). `Date.now()` returns a timestamp in milliseconds (i.e 13 digits: `1655373910837`). Make sure to convert between milliseconds and nanoseconds to properly handle time variables. In Rust `std::time::SystemTime::now()` returns a `SystemTime` object. You can convert it to nanoseconds since UNIX_EPOCH as follows: ```rust use std::time::{SystemTime, UNIX_EPOCH}; let start = SystemTime::now(); let since_the_epoch = start.duration_since(UNIX_EPOCH) .expect("Time went backwards"); let in_nanoseconds = since_the_epoch.as_nanos(); println!("Current time in nanoseconds since UNIX EPOCH: {}", in_nanoseconds); ``` --- ## Deposits Smart contracts handle NEAR amounts always using `yoctoNEAR` (1Ⓝ = 10^24yocto). This means that when interacting with contract's or the user's balance you will need to convert between NEAR and yoctoNEAR. Under the hood, the network (and contracts) represent balances as `u128`, which are encoded as strings (since JSON cannot handle more than 52 bit integers). Remember to **always** send amounts as `strings` (e.g. `"1000000000000000000000000"` for 1Ⓝ) and **never** as `number` (e.g. `1000000`). Consequently, convert amounts from `string` whenever you want to display them to the user. In javascript, you can use the `near-api-js` library to convert between NEAR and yoctoNEAR: ```js const units = NEAR.fromDecimal('1.5') // 1.5Ⓝ console.log(units) // "1500000000000000000000000" const decimal = NEAR.fromUnits('1500000000000000000000000', 2) console.log(decimal) // 1.5 ``` In Rust you can directly use the `near_token` crate to convert between NEAR and yoctoNEAR: ```rust use near_token::NearToken fn main() { const TEN_NEAR: NearToken = NearToken::from_near(10); assert_eq!(TEN_NEAR.to_string(), "10.00 NEAR"); assert_eq!(TEN_NEAR.as_near(), 10); assert_eq!(TEN_NEAR.as_millinear(), 10000); assert_eq!(TEN_NEAR.as_yoctonear(), 10000000000000000000000000); let input_str = "0.123456 NEAR"; let input_near: NearToken = input_str.parse().unwrap(); assert_eq!( input_near, NearToken::from_yoctonear(123456000000000000000000) ); } ``` --- ## Gas When calling a contract method, you can specify how much gas you want to attach to the call. Within the network, the `Gas` is represented as a `u64`, which is encoded as a `string` (since JSON cannot handle more than 52 bit integers). As a rule of thumb, functions that consume little computation can be called with `30 Tgas` or less, while more complex functions may require up to `300 Tgas`. In javascript there is no built-in library to handle gas units, but you can easily convert between them as follows: ```js const TGAS = 1000000000000; // 1 Tgas = 10^12 gas units function toTgas(gas) { return (gas * TGAS).toString(); } function fromTgas(tgas) { return (parseInt(tgas) / TGAS).toFixed(2); } ``` ```rust use near_gas::NearGas; fn main() { let data = "12.657 tgas"; let near_gas: NearGas = data.parse().unwrap(); // Convert the value to the most precise "gas" unit assert_eq!(near_gas.as_gas(), 12657000000000); // Convert the value to "gigagas" unit assert_eq!(near_gas.as_ggas(), 12657); // Display Gas. It will print: "Here is 12.7 Tgas" println!("Here is {}", near_gas); // When `serde` feature is enabled, NearGas can be used in serde-serializable structs. // NearGas will be serialized to a gas-precision u64 value encoded as string. #[derive(serde::Serialize)] struct FunctionCallDetails { used_gas: NearGas, } let details = FunctionCallDetails { used_gas: near_gas }; assert_eq!(serde_json::to_string(&details).unwrap(), r#"{"used_gas":"12657000000000"}"#); } ``` --- # Source: https://docs.near.org/primitives/liquid-staking/deploy-your-own-contract.md --- id: deploy-your-own-contract title: Deploying Your Own Contract description: "Learn how to deploy your own Liquid Staking Contract on NEAR" --- At its core, the Liquid Staking Contract issues a token that follows the [Fungible Token (NEP-141)](https://github.com/near/NEPs/blob/master/neps/nep-0141.md) standard. When you stake `NEAR`, you don’t just lock it away — you receive a liquid token (for example, `rNEAR`) that behaves like any other fungible token on the network. You can transfer it, swap it, or use it inside DeFi protocols exactly the same way you would with any other token. And, because it’s fully NEP-141 compatible, this token integrates seamlessly with wallets, DEXs, and other smart contracts. That compatibility is what enables the early-exit mechanism — you can instantly convert your liquid token back to `NEAR` by swapping it on an exchange. The swap rate is determined by market supply and demand and usually sits slightly below the on-chain rate (roughly 0.5% lower), which represents the small cost of immediate liquidity. Behind the scenes, the exchange rate of the liquid token is continuously adjusted based on rewards earned by validators. It’s calculated as `exchange_rate = total_staked_near / total_liquid_token_supply`​ and as staking rewards accumulate each epoch, the total amount of staked `NEAR` grows, while the token supply changes only when users deposit or withdraw. That’s why the exchange rate, and therefore the token’s value, steadily increases over time, even if you hold the same balance. --- ## Storage Deposit (NEP-145) Because the liquid staking contract issues a [NEP-141 token](https://github.com/near/NEPs/blob/master/neps/nep-0141.md), it also implements the [Storage Management (NEP-145)](https://github.com/near/NEPs/blob/master/neps/nep-0145.md) standard. Whenever you interact with the contract for the first time, for example, when you deposit `NEAR` and get liquid tokens in return, you need to attach a tiny storage deposit. This deposit covers the space your account information takes up in the contract’s storage. The good thing is that this money isn’t spent, it just stays reserved while you’re using the contract. And if you ever stop using it, you can get your deposit back by unregistering your account. In short, it’s a way to make sure the contract doesn’t pay for users’ data out of its own balance, while still letting everyone safely recover their deposit whenever they leave. --- ## Deploying a Liquid Staking Contract The Liquid Staking Contract can be deployed directly from the [repository](https://github.com/ref-finance/rnear-contract), which contains the implementation maintained by the Rhea.finance team. This contract follows the same principles described above and is already optimized for production use. Before deployment, make sure the target account has at least 20.5 `NEAR` available on its balance. This amount covers the initial storage costs, the minimum staking liquidity required for initialization, and a small gas buffer. ```bash # Clone the repository git clone https://github.com/ref-finance/rnear-contract.git cd rnear-contract/contracts/lst ## Create and fund the liquid staking account near create-account --useAccount --initialBalance # Build and deploy the liquid staking contract cargo near deploy build-non-reproducible-wasm with-init-call new json-args '{ "metadata": { "decimals": 24, "name": "Test Liquid Near Staking Token", "spec": "ft-1.0.0", "symbol": "tNEAR" }, "owner_id": "owner.near" }' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' ```
### Setting Validator Whitelist Account Before you can add validators, you first need to set the validator whitelist account. This account acts as a single source of truth for which validators are allowed to be used by the contract. The validator whitelist account must implement the following interface that allows the contract to verify if a given validator is approved. ```rust #[ext_contract(ext_whitelist)] pub trait ExtWhitelist { fn is_whitelisted(&self, staking_pool_account_id: AccountId) -> bool; } ``` To update the validator whitelist account, please run the following command: ```bash near contract call-function as-transaction set_whitelist_contract_id json-args '{ "account_id": "whitelist_source.near" }' prepaid-gas '30.0 Tgas' attached-deposit '1 yoctoNEAR' ```
### Adding Validators After the whitelist account is in place, you can register a list of validators that the contract will delegate tokens to, along with their weights. Each weight defines a proportion of how much of the total stake should go to a particular validator compared to the others. To add validators, please run the following comand: ```bash near contract call-function as-transaction add_validators json-args '{ "validator_ids": ["validator1.pool.near", "validator2.pool.near", "validator3.pool.near"], "weights": [40, 35, 25] }' prepaid-gas '30.0 Tgas' attached-deposit '1 yoctoNEAR' ``` --- ## Token In this section, we’ll go through a few of the most commonly used methods. To explore all available methods, please refer to the full definition of [NEP-141](https://github.com/near/NEPs/blob/master/neps/nep-0141.md).
### Storage Deposit Before you can hold or receive any liquid tokens, your account needs to be registered in the contract’s storage. You only need to do this once, and the amount is fully refundable if you ever unregister your account. To register the account, run the following command: ```bash near contract call-function as-transaction storage_deposit json-args '{ "account_id":"", "registration_only": true }' prepaid-gas '30.0 Tgas' attached-deposit '0.00125 NEAR' ```
### Check Balance To check how many liquid tokens (e.g., `tNEAR`) you own, run the following command: ```bash near contract call-function as-read-only ft_balance_of json-args '{"account_id": ""}' ```
### Check Total Supply To check the total supply of the liquid token (e.g., `tNEAR`), run the following command: ```bash near contract call-function as-read-only ft_total_supply json-args '{}' ```
### Transfer Tokens To transfer tokens to another account directly, run the following command: ```bash near contract call-function as-transaction ft_transfer json-args '{ "amount": "10000000000000000000000000", "receiver_id":"" }' prepaid-gas '30.0 Tgas' attached-deposit '1 yoctoNEAR' ```
### Check Token Price While most methods follow the NEP-141 standard, the liquid staking contract also provides an additional helper method called `ft_price`. This method isn’t part of the standard itself — it’s implemented specifically for this contract to make it easier to check the current exchange rate between the liquid token and `NEAR`, since the price of the token is determined by the formula: `exchange_rate = total_staked_near / total_liquid_token_supply` To query the current price, run the following command: ```bash near contract call-function as-read-only ft_price json-args '{}' ``` The returned number shows how much `yoctoNEAR` one unit of the liquid token is currently worth. --- ## Additional Resources For more information, explore the official resources below: - [Liquid Staking Contract Repository](https://github.com/ref-finance/rnear-contract) - [NEAR CLI](https://github.com/near/near-cli-rs) --- # Source: https://docs.near.org/tutorials/auction/deploy.md # Source: https://docs.near.org/smart-contracts/release/deploy.md --- id: deploy title: Deploying sidebar_label: Deploying and Using description: "Deploy a contract to the network." --- After your contract is ready you can deploy it in the NEAR network for everyone to use it. Let us guide you on how to use the [NEAR CLI](../../tools/cli.md) to deploy your contract and call its methods. :::info On this page, we will only cover the basics of NEAR CLI. For more information visit the [NEAR CLI documentation page](../../tools/cli.md). ::: --- ## Deploying the Contract Thanks to the `NEAR CLI` deploying a contract is as simple as: 1. Compiling the contract to wasm. 2. [Create an account](../../tools/cli.md#create) and [deploy the contract](../../tools/cli.md#deploy) into it using `NEAR CLI`. ### Compile the Contract ```bash cargo near build ``` ```bash yarn build ``` ### Create an Account and Deploy ```bash # Create a new account pre-funded by a faucet near create-account --useFaucet # deploy the contract near deploy ``` ### Deploy in an Existing Account ```bash # login into your account near login # deploy the contract near deploy ``` :::tip You can overwrite a contract by deploying another on top of it. In this case, the account's logic will change, but the state will persist ::: :::info By default `near-cli` uses the `testnet` network. Define `NEAR_ENV=mainnet` to deploy into `mainnet`. ::: :::info Naming Convention for Public-Facing Methods Once the contract is deployed to the network, anyone and any other contract (i.e., any other account on NEAR) can interact with it by calling its methods. Furthermore, any transactions involving the contract will also be included in the network's data stream, which means its activity can also be visible to any who listens to particular events. Considering this, we advise to name methods using `snake_case` in all SDKs as this is compatible with the remainder of the NEAR ecosystem which is predominantly comprised of Rust contracts. ::: --- ## Initializing the Contract If your contract has an [initialization method](../anatomy/storage.md) you can call it to initialize the state. This is not necessary if your contract implements `default` values for the state. ```bash # Call the initialization method (`init` in our examples) near call [] --useAccount ``` :::info You can initialize your contract [during deployment](#deploying-the-contract) using the `--initFunction` & `--initArgs` arguments. ::: --- ## Calling the Contract Once your contract is deployed you can interact with it right away using [NEAR CLI](../../tools/cli.md).
### View methods View methods are those that perform **read-only** operations. Calling these methods is free, and do not require to specify which account is being used to make the call: ```bash near view ``` :::tip View methods have by default 200 TGAS for execution :::
### Change methods Change methods are those that perform both read and write operations. For these methods we do need to specify the account being used to make the call, since that account will expend GAS in the call. ```bash near call --useAccount [--deposit ] [--gas ] ``` --- # Source: https://docs.near.org/ai/shade-agents/tutorials/ai-dao/deploying.md # Source: https://docs.near.org/ai/shade-agents/getting-started/quickstart/deploying.md --- id: deploying title: Deploying an Agent sidebar_label: Deploying an Agent description: "Learn how to quickly deploy your first Shade Agent." --- :::warning The Shade Agent Framework is experimental and contains known critical vulnerabilities. It must not be used in production or on mainnet and is intended solely for testing and non-critical use on testnet. No representations or warranties are made regarding security, correctness, or fitness for any purpose. Use of this software is entirely at your own risk. A production-ready version of the framework is currently in development. ::: In this section, we'll walk you through deploying a Shade Agent. The Shade Agent Framework abstracts the complexities of creating a agent by removing TEE specific code and handling the deployment of the agent contract under hood. The [template](https://github.com/NearDeFi/shade-agent-template) we're using is a simple Shade Agent built with Hono and written in TypeScript that acts as a verifiable ETH price oracle. It fetches the price of Eth from two different APIs, takes the average, and then pushes the price to an Ethereum contract. We'll cover two deployment scenarios: 1. **Local Development**: Running the agent locally for rapid testing and development. 2. **TEE Deployment**: Running the agent in a real Trusted Execution Environment (TEE). On the [next page](./components.md), you'll see how to edit this agent for your specific use case. --- ## Prerequisites - Install NEAR and Shade Agent tooling: ```bash # Install the NEAR CLI curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh # Install the Shade Agent CLI npm i -g @neardefi/shade-agent-cli ``` - Create a `NEAR testnet account` and record the account name and `seed phrase`: ```bash export ACCOUNT_ID=example-name.testnet near account create-account sponsor-by-faucet-service $ACCOUNT_ID autogenerate-new-keypair save-to-keychain network-config testnet create ``` replacing `example-name.testnet` with a unique account Id. - Set up Docker if you have not already: - Install Docker for [Mac](https://docs.docker.com/desktop/setup/install/mac-install/) or [Linux](https://docs.docker.com/desktop/setup/install/linux/) and create an account. - Log in to Docker, using `docker login` for Mac or `sudo docker login` for Linux. - Set up a free Phala Cloud account at https://cloud.phala.network/register, then get an API key from https://cloud.phala.network/dashboard/tokens.
What is a Phala Cloud Phala Cloud is a cloud service that supports hosting applications in a TEE. It makes it easy to run an agent in TEE.
--- ## Set up - First, `clone` the [template](https://github.com/NearDeFi/shade-agent-template). ```bash git clone https://github.com/NearDeFi/shade-agent-template cd shade-agent-template ``` - Rename the `.env.development.local.example` file name to `.env.development.local` and configure your environment variables. - Start up Docker: ```bash sudo systemctl start docker ``` Simply open the Docker Desktop application or run: ```bash open -a Docker ``` - Install dependencies ```bash npm i ``` --- ## Local Development - Make sure the `NEXT_PUBLIC_contractId` prefix is set to `ac-proxy.` followed by your NEAR accountId. - In one terminal, run the Shade Agent CLI: ```bash shade-agent-cli ``` The CLI on Linux may prompt you to enter your `sudo password`. - In another terminal, start your app: ```bash npm run dev ``` Your app will start on http://localhost:3000 --- ## TEE Deployment - Change the `NEXT_PUBLIC_contractId` prefix to `ac-sandbox.` followed by your NEAR accountId. - Run the Shade Agent CLI ```bash shade-agent-cli ``` The CLI on Linux may prompt you to enter your `sudo password`. The last `URL` the CLI outputs is the URL of your app. If your application is not working, head over to your App on the Phala Dashboard and review the logs. After deploying to Phala Cloud, monitor your deployments and delete unused ones to avoid unnecessary costs. You can manage your deployments from the[dashboard](https://cloud.phala.network/dashboard). --- ## Interacting with the Agent You can interact with your agent via the APIs directly or via a lightweight frontend contained in this repo. ### Direct For Phala deployments, swap localhost:3000 for your deployment URL. - Get the Agent account ID and its balance: ``` http://localhost:3000/api/agent-account ``` - Get the derived Ethereum Sepolia price pusher account ID and its balance (you will need to fund this account): ``` http://localhost:3000/api/eth-account ``` - Send a transaction through the agent to update the price of Eth: ``` http://localhost:3000/api/transaction ``` ### Frontend To start the frontend, run: ```bash cd frontend npm i npm run dev ``` To use the frontend with your Phala deployment change the `API_URL` to Phala URL in your [config.js](https://github.com/NearDeFi/shade-agent-template/blob/main/frontend/src/config.js) file. --- ## Next Steps Now that you've successfully deployed your first Shade Agent, proceed to the [key components page](./components.md) to understand how agents work and learn how to customize it for your specific use case. --- # Source: https://docs.near.org/primitives/dex.md --- id: dex title: Decentralized Exchanges (DEX) description: "Learn how to interact with decentralized exchanges on NEAR Protocol, including token swapping, liquidity pools, and integration with Ref Finance DEX." --- A Decentralized Exchange (DEX) is an application that allows users to trade tokens (native & fungible tokens) through smart contracts. ![dex](/assets/docs/primitives/dex.png) In brief, DEXs work by having [pools of token pairs](https://guide.rhea.finance/products/overview/pooling) (e.g. NEAR-USDC) that users can deposit tokens into. The ratio of the tokens in the pool determines the exchange rate for a swap. Indeed, swapping is adding tokens to one side of the pool while removing tokens from the other side of the pool. :::info This docs refer to [Ref Finance](https://www.ref.finance/), a community built DEX in NEAR. Please check their [docs](https://guide.rhea.finance/) for more information. ::: --- ## Query Token Exchange Rate One can query the exchange rate of a token pair by calling the `get-token-price` method on the DEX contract. ```js const tokenContract = 'token.v2.ref-finance.near'; const tokenPriceResult = await fetch( `https://indexer.ref.finance/get-token-price?token_id=${tokenContract}`, ); const tokenPriceValue = await tokenPriceResult.json(); ```
Example response

```json { "token_contract_id": "token.v2.ref-finance.near", "price": "0.08153090" } ```

:::tip Ref Finance has a method to [get all token prices at once](https://indexer.ref.finance/list-token-price). :::
--- ## Query Whitelisted Tokens Anyone list tokens for sale in the DEX. This is why, in order to protect users, the DEX contract has a list of whitelisted tokens that can be traded. ```bash near view v2.ref-finance.near get_whitelisted_tokens ```
Examples Response ```bash 'wrap.near', 'usdt.tether-token.near', 'berryclub.ek.near', 'farm.berryclub.ek.near', 'token.v2.ref-finance.near', 'token.paras.near', 'marmaj.tkn.near', 'meta-pool.near', ... ```
}>
Examples Response ```bash 'wrap.near', 'usdt.tether-token.near', 'berryclub.ek.near', 'farm.berryclub.ek.near', 'token.v2.ref-finance.near', 'token.paras.near', 'marmaj.tkn.near', 'meta-pool.near', ... ```
--- ## Register in the DEX In order to use the contract, make sure to register your account in the DEX by paying for the storage you will use in order to keep track of your balances. ```bash near call v2.ref-finance.near storage_deposit '' --useAccount --amount 0.1 ``` }> --- ## Deposit funds In order to swap tokens, one must first deposit tokens into the DEX. For this, you will need to transfer the FT you want to swap to the DEX contract. ```bash near call token.v2.ref-finance.near ft_transfer_call {"receiver_id": "v2.ref-finance.near", "amount": "1000000000000", "msg": ""} --gas 300000000000000 --depositYocto 1 --useAccount ``` }> :::danger Do **NOT** transfer **NEAR** tokens to Ref Finance. Instead, call `near_deposit` in the [`wrap.near`](https://nearblocks.io/address/wrap.near) contract, attaching the amount of NEAR you want to swap. This will mint `wrap.near` for you, which you can then transfer to Ref Finance. ::: --- ## Get Deposit Balances Query your deposit balances by calling the `get_deposits` method: ```js import { useNearWallet } from "near-connect-hooks"; const AMM_CONTRACT_ADDRESS = 'v2.ref-finance.near'; const { viewFunction } = useNearWallet(); await viewFunction({ method: 'get_deposits', args: { account_id: 'bob.near', }, contractId: AMM_CONTRACT_ADDRESS, }); ``` Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application
Example response

```json { "token.v2.ref-finance.near": "0", "wrap.near": "0" } ```

```bash near view v2.ref-finance.near get_deposits '{"account_id": "bob.near"}' ```
Example response

```bash { 'token.v2.ref-finance.near': '0', 'wrap.near': "0" } ```

}>
Example response

```bash { 'token.v2.ref-finance.near': '0', 'wrap.near': "0" } ```

```rust // Validator interface, for cross-contract calls #[ext_contract(ext_amm_contract)] trait ExternalAmmContract { fn get_deposits(&self, account_id: AccountId) -> Promise; } // Implement the contract structure #[near] impl Contract { #[private] // Public - but only callable by env::current_account_id() pub fn external_get_deposits_callback(&self, #[callback_result] call_result: Result, PromiseError>) -> Option> { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting external contract"); return None; } // Return the pools data let deposits_data = call_result.unwrap(); return Some(deposits_data); } pub fn get_contract_deposits(&self) -> Promise { let promise = ext_amm_contract::ext(self.amm_contract.clone()) .get_deposits(env::current_account_id()); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .external_get_deposits_callback() ) } } ```
--- ### Query Pools DEXs work by having multiple pools of token pairs (e.g. NEAR-USDC) that users can deposit tokens into. ```js import { useNearWallet } from "near-connect-hooks"; const AMM_CONTRACT_ADDRESS = 'v2.ref-finance.near'; const { viewFunction } = useNearWallet(); await viewFunction({ method: 'get_pools', args: { from_index: 0, limit: 1000, }, contractId: AMM_CONTRACT_ADDRESS, }); ``` Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application
Example response

```js [ { pool_kind: 'SIMPLE_POOL', token_account_ids: ['token.skyward.near', 'wrap.near'], amounts: ['51865812079751349630100', '6254162663147994789053210138'], total_fee: 30, shares_total_supply: '1305338644973934698612124055', amp: 0, }, { pool_kind: 'SIMPLE_POOL', token_account_ids: [ 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.factory.bridge.near', 'wrap.near', ], amounts: ['783621938569399817', '1100232280852443291118200599'], total_fee: 30, shares_total_supply: '33923015415693335344747628', amp: 0, }, ]; ```

```bash near view v2.ref-finance.near get_pools '{"from_index": 0, "limit": 1000}' ```
Example response

```bash [ { pool_kind: 'SIMPLE_POOL', token_account_ids: [ 'token.skyward.near', 'wrap.near' ], amounts: [ '51865812079751349630100', '6254162663147994789053210138' ], total_fee: 30, shares_total_supply: '1305338644973934698612124055', amp: 0 }, { pool_kind: 'SIMPLE_POOL', token_account_ids: [ 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.factory.bridge.near', 'wrap.near' ], amounts: [ '783621938569399817', '1100232280852443291118200599' ], total_fee: 30, shares_total_supply: '33923015415693335344747628', amp: 0 } ] ```

}>
Example response

```bash [ { pool_kind: 'SIMPLE_POOL', token_account_ids: [ 'token.skyward.near', 'wrap.near' ], amounts: [ '51865812079751349630100', '6254162663147994789053210138' ], total_fee: 30, shares_total_supply: '1305338644973934698612124055', amp: 0 }, { pool_kind: 'SIMPLE_POOL', token_account_ids: [ 'c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2.factory.bridge.near', 'wrap.near' ], amounts: [ '783621938569399817', '1100232280852443291118200599' ], total_fee: 30, shares_total_supply: '33923015415693335344747628', amp: 0 } ] ```

```rust #[near(serializers = [json])] pub struct PoolInfo { /// Pool kind. pub pool_kind: String, /// List of tokens in the pool. pub token_account_ids: Vec, /// How much NEAR this contract has. pub amounts: Vec, /// Fee charged for swap. pub total_fee: u32, /// Total number of shares. pub shares_total_supply: U128, pub amp: u64, } // Validator interface, for cross-contract calls #[ext_contract(ext_amm_contract)] trait ExternalAmmContract { fn get_pools(&self, from_index: u64, limit: u64) -> Promise; } // Implement the contract structure #[near] impl Contract { #[private] // Public - but only callable by env::current_account_id() pub fn external_get_pools_callback(&self, #[callback_result] call_result: Result, PromiseError>) -> Option> { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting external contract"); return None; } // Return the pools data let pools_data = call_result.unwrap(); return Some(pools_data); } pub fn get_amm_pools(&self, from_index: u64, limit: u64) -> Promise { let promise = ext_amm_contract::ext(self.amm_contract.clone()) .get_pools(from_index, limit); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .external_get_pools_callback() ) } } ```
--- ## Swap tokens In order to swap a token for another, you need to [have funds](#deposit-funds), and there needs to [**exist a pool**](#query-pools) that has **both tokens** on it. ```js import { useNearWallet } from "near-connect-hooks"; const AMM_CONTRACT_ADDRESS = 'v2.ref-finance.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: AMM_CONTRACT_ADDRESS, method: 'swap', args: { actions: [ { pool_id: 79, token_in: 'token.v2.ref-finance.near', token_out: 'wrap.near', amount_in: '100000000000000000', min_amount_out: '1', }, ], }, gas: 300000000000000, deposit: 1, }); ``` Learn more about adding [Near Connect](../web3-apps/tutorials/wallet-login) to your application
Example response ```json "5019606679394603179450" ```
```bash near call v2.ref-finance.near swap "{\"actions\": [{\"pool_id\": 79, \"token_in\": \"token.v2.ref-finance.near\", \"amount_in\": \"100000000000000000\", \"token_out\": \"wrap.near\", \"min_amount_out\": \"1\"}]}" --gas 300000000000000 --depositYocto 1 --useAccount bob.near ```
Example response

```bash '5019606679394603179450' ```

}>
Example response

```bash '5019606679394603179450' ```

```rust #[near(serializers = [json])] pub struct SwapAction { /// Pool which should be used for swapping. pub pool_id: u64, /// Token to swap from. pub token_in: AccountId, /// Amount to exchange. /// If amount_in is None, it will take amount_out from previous step. /// Will fail if amount_in is None on the first step. pub amount_in: Option, /// Token to swap into. pub token_out: AccountId, /// Required minimum amount of token_out. pub min_amount_out: U128, } // Validator interface, for cross-contract calls #[ext_contract(ext_amm_contract)] trait ExternalAmmContract { fn swap(&self, actions: Vec) -> Promise; } // Implement the contract structure #[near] impl Contract { #[private] // Public - but only callable by env::current_account_id() pub fn external_call_callback(&self, #[callback_result] call_result: Result) { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting external contract"); } } #[payable] pub fn swap_tokens(&mut self, pool_id: u64, token_in: AccountId, token_out: AccountId, amount_in: U128, min_amount_out: U128) -> Promise { assert_eq!(env::attached_deposit(), 1, "Requires attached deposit of exactly 1 yoctoNEAR"); let swap_action = SwapAction { pool_id, token_in, token_out, amount_in: Some(amount_in), min_amount_out }; let mut actions = Vec::new(); actions.push(swap_action); let promise = ext_amm_contract::ext(self.amm_contract.clone()) .with_static_gas(Gas(150*TGAS)) .with_attached_deposit(YOCTO_NEAR) .swap(actions); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(Gas(100*TGAS)) .external_call_callback() ) } } ```
--- ## Additional Resources 1. [Claim Fungible Tokens from Lockup](https://near.org/near/widget/ComponentDetailsPage?src=whtt.near/widget/Draft-0) - the example how to claim locked tokens from the `lockup.burrow.near` contract. 2. [BSC Dex Collection](https://near.org/near/widget/ComponentDetailsPage?src=bluebiu.near/widget/Bsc.Swap.Dex) - the example of how to build simple swap page for a DEX. --- # Source: https://docs.near.org/primitives/did.md --- id: did title: Decentralized Identifiers (DIDs) description: "Learn about W3C-compliant identity resolution on NEAR." --- This document details how to use the `did:near` method for decentralized identifiers on the NEAR blockchain, how it complies with [W3C DID Core](https://www.w3.org/TR/did-core/), and how it integrates with verifiable credentials (VCs), proof registries, and resolution tooling. --- ## What is a DID? A **Decentralized Identifier (DID)** is a persistent, unique identifier that does not require a centralized registry. On NEAR, DIDs are created based on either account names or raw public keys, and they are fully compatible with the W3C DID standard.
Authors Built in collaboration with NTT DATA Innovation Center Web3
--- ## The `did:near` Method This method supports two types of DIDs with different resolution strategies: | Type | Example | Description | |--------------------|-----------------------------------------|--------------------------------------| | Named Account | `did:near:alice.testnet` or `did:near:alice.near` | Resolved directly from on-chain NEAR account keys (FullAccess keys) | | Registry DID (Base58) | `did:near:CF5RiJYh4EVmEt8UADTjoP3XaZo1NPWxv6w5TmkLqjpR` | Resolved via smart contract registry using `identity_owner` method | --- ## DID Document Format Example output from resolving a `did:near`: ```json { "@context": [ "https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/ed25519-2018/v1" ], "id": "did:near:alice.testnet", "verificationMethod": [ { "id": "did:near:alice.testnet#owner", "type": "Ed25519VerificationKey2018", "controller": "did:near:alice.testnet", "publicKeyBase58": "7WLUHT69sw5UpYK9xAY5cbdWKf4vSMruXzwfbL999zXo" } ], "authentication": ["did:near:alice.testnet#owner"], "assertionMethod": ["did:near:alice.testnet#owner"] } ``` This structure follows the [W3C DID Core Spec](https://www.w3.org/TR/did-core/), including: - A DID identifier (`id`) - Key references (`verificationMethod`) - Authentication and assertion capabilities - Base58-encoded Ed25519 public keys --- ## ⚙️ Repositories | Component | Repository URL | |----------------|----------------| | DID Registry (contract) | https://github.com/DTI-web3/did-near | | ProofType Contract | https://github.com/DTI-web3/did-near-prooftype | | ProofType SDK | https://www.npmjs.com/package/@kaytrust/prooftypes | | DID Resolver SDK | https://github.com/DTI-web3/did-near-resolver | --- ## How Resolution Works The resolver uses different strategies based on DID format: ### Named Account DIDs (`did:near:alice.testnet`) 1. Parse the account ID from the DID (e.g., `alice.testnet`). 2. Determine the network from the suffix (`.testnet` → testnet, `.near` → mainnet). 3. Query the NEAR RPC using `near-api-js` to fetch account access keys. 4. Filter for keys with `permission: "FullAccess"`. 5. Construct the DID document with all FullAccess public keys as verification methods. ### Registry DIDs (`did:near:CF5RiJYh4EVmEt8UADTjoP3XaZo1NPWxv6w5TmkLqjpR`) 1. Identify the DID as Base58 format (44-50 character string matching `[1-9A-HJ-NP-Za-km-z]+`). 2. Call the `identity_owner` view method on the configured registry contract. 3. Retrieve the owner DID registered for that Base58 identifier. 4. Construct the DID document with the public key from the registry. --- ## How to Use ### Create a DID #### Named Account DID (Direct Resolution) If using a NEAR wallet account, the DID is derived directly from the account name: ```ts const did = "did:near:yourname.testnet"; // For testnet const did = "did:near:yourname.near"; // For mainnet ``` #### Registry DID (Contract-Based) If using a Base58-encoded identifier registered in a smart contract: ```ts const did = "did:near:CF5RiJYh4EVmEt8UADTjoP3XaZo1NPWxv6w5TmkLqjpR"; ``` #### Network-Specific DIDs You can explicitly specify the network: ```ts const did = "did:near:testnet:CF5RiJYh4EVmEt8UADTjoP3XaZo1NPWxv6w5TmkLqjpR"; // Explicitly targets testnet const did = "did:near:near:CF5RiJYh4EVmEt8UADTjoP3XaZo1NPWxv6w5TmkLqjpR"; // Explicitly targets mainnet ``` --- ## Issuing and Verifying Credentials ### Step 1 – Hash the Credential ```ts const cid = SHA256(JSON.stringify(credential)).toString(CryptoJS.enc.Base64); ``` ### Step 2 – Issue on NEAR ```ts await proofType.generateProof(credential, { wallet, cid }); ``` This records the hash (`cid`) on-chain with the `subject_did`. ### Step 3 – Verify On-chain ```ts const isValid = await proofType.verifyProof("did:near:subject|bafy..."); // returns true or false ``` --- ## SDK Usage ### Resolver SDK #### Standalone Usage ```ts const resolver = new NearDIDResolver({ networks: [ { networkId: 'testnet', rpcUrl: 'https://rpc.testnet.near.org', contractId: 'neardidregistry.testnet' // Optional: only for Registry DIDs }, { networkId: 'mainnet', // You can use 'near' for mainnet (or 'mainnet', both are equivalent) rpcUrl: 'https://rpc.mainnet.near.org', contractId: 'neardidregistry.near' // Optional } ] }); const doc = await resolver.resolveDID("did:near:alice.testnet"); console.log(doc.id); // "did:near:alice.testnet" ``` #### Integration with `did-resolver` ```ts const nearResolver = getResolver({ networks: [ { networkId: 'testnet', rpcUrl: 'https://rpc.testnet.near.org', contractId: 'neardidregistry.testnet' } ] }); const resolver = new Resolver({ ...nearResolver, // ... other DID methods }); const result = await resolver.resolve('did:near:alice.testnet'); console.log(result.didDocument); ``` ### ProofType SDK ```ts const proofType = new ProofTypeNear(); const proof = await proofType.generateProof(vcPayload, { wallet, cid }); const isValid = await proofType.verifyProof(proof); ``` --- ## Security & Compliance - Each `(did, cid)` pair can only be registered once. - Issuer is linked to the signer account via NEAR transaction. - DID resolution and structure is fully W3C-compliant. - `cid` is always a Base64-encoded SHA-256 hash of the VC content. - **Key Security**: Only `FullAccess` keys are included in the DID document's `verificationMethod` array, ensuring that only keys with complete account authority are used for authentication. - Public keys are handled securely via NEAR native accounts or custom registries. - **Multi-Network Support**: The resolver automatically routes requests to the correct network (mainnet/testnet) based on the DID suffix. --- ## Testnet Deployment - Registry Contract: `neardidregistry.testnet` - ProofType Contract: `neardtiprooftype.testnet` - Network: `testnet` - Language: Rust (`near-sdk`) --- ## Conclusion The `did:near` method enables full-stack decentralized identity flows on NEAR, including: - Account- and key-based identifiers - W3C-compliant DID documents - Smart contract-backed credential proofs - SDKs for resolution and attestation Use this stack to build portable, verifiable, and secure Web3 identity solutions on NEAR. --- # Source: https://docs.near.org/tutorials/examples/donation.md --- id: donation title: Donation description: "Learn to build a donation smart contract that accepts NEAR tokens, tracks donations, and distributes funds to beneficiaries." --- 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. ``` import { utils } from "near-api-js"; import { useEffect, useState } from "react"; import { useWalletSelector } from '@near-wallet-selector/react-hook'; import { DonationNearContract } from "@/config"; const DonationsTable = () => { const { signedAccountId, viewFunction } = useWalletSelector(); const [donations, setDonations] = useState([]); const [currentPage, setCurrentPage] = useState(1); const [lastPage, setLastPage] = useState(0); const donationsPerPage = 5; const getDonations = async (page) => { const number_of_donors = await viewFunction({ contractId: DonationNearContract, method: "number_of_donors", }); setLastPage(Math.ceil(number_of_donors / donationsPerPage)); const fromIndex = (page - 1) * donationsPerPage; const donations = await viewFunction({ contractId: DonationNearContract, method: "get_donations", args: { from_index: fromIndex.toString(), limit: donationsPerPage.toString(), }, }); return donations; }; useEffect(() => { if (!signedAccountId) return; getDonations(currentPage).then((loadedDonations) => setDonations(loadedDonations), ); }, [signedAccountId, currentPage]); const goToNextPage = () => { setCurrentPage((prevPage) => prevPage + 1); }; const goToPrevPage = () => { setCurrentPage((prevPage) => prevPage - 1); }; return (
{donations.map((donation) => ( ))}
User Total Donated Ⓝ
{donation.account_id} {utils.format.formatNearAmount(donation.total_amount)}
Page {currentPage}
); }; export default DonationsTable; ``` ``` import { utils } from "near-api-js"; import { useState } from "react"; import { useWalletSelector } from '@near-wallet-selector/react-hook'; import { DonationNearContract } from "@/config"; const DonationForm = ({ setMyDonation }) => { const { callFunction } = useWalletSelector(); const [amount, setAmount] = useState(0); const setDonation = async (amount) => { let data = await fetch( "https://api.coingecko.com/api/v3/simple/price?ids=near&vs_currencies=usd", ).then((response) => response.json()); const near2usd = data["near"]["usd"]; const amount_in_near = amount / near2usd; const rounded_two_decimals = Math.round(amount_in_near * 100) / 100; setAmount(rounded_two_decimals); }; const handleSubmit = async (event) => { event.preventDefault(); let deposit = utils.format.parseNearAmount(amount.toString()); callFunction({ contractId: DonationNearContract, method: "donate", deposit, }) .catch(() => { setMyDonation(-Number(amount)); }); setMyDonation(amount); }; return ( <>
{[10, 20, 50, 100].map((amount) => (
))}
setAmount(e.target.value)} className="form-control" />
); }; export default DonationForm; ```
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`). ``` init({ beneficiary }: { beneficiary: string }) { this.beneficiary = beneficiary } @call({ payableFunction: true }) donate() { // Get who is calling the method and how much $NEAR they attached let donor = near.predecessorAccountId(); let donationAmount: bigint = near.attachedDeposit() as bigint; let donatedSoFar = this.donations.get(donor, { defaultValue: BigInt(0) }) let toTransfer = donationAmount; // This is the user's first donation, lets register it, which increases storage if (donatedSoFar == BigInt(0)) { assert(donationAmount > STORAGE_COST, `Attach at least ${STORAGE_COST} yoctoNEAR`); // Subtract the storage cost to the amount to transfer toTransfer -= STORAGE_COST } // Persist in storage the amount donated so far donatedSoFar += donationAmount this.donations.set(donor, donatedSoFar) near.log(`Thank you ${donor} for donating ${donationAmount}! You donated a total of ${donatedSoFar}`); // Send the money to the beneficiary const promise = near.promiseBatchCreate(this.beneficiary) near.promiseBatchActionTransfer(promise, toTransfer) ``` ``` #[payable] pub fn donate(&mut self) -> String { // Get who is calling the method and how much NEAR they attached let donor: AccountId = env::predecessor_account_id(); let donation_amount = env::attached_deposit(); require!( donation_amount > STORAGE_COST, format!( "Attach at least {} yoctoNEAR to cover for the storage cost", STORAGE_COST ) ); let mut donated_so_far: NearToken = self .donations .get(&donor) .cloned() .unwrap_or(NearToken::from_near(0)); let to_transfer = if donated_so_far.is_zero() { // This is the user's first donation, lets register it, which increases storage // Subtract the storage cost to the amount to transfer donation_amount.saturating_sub(STORAGE_COST).to_owned() } else { donation_amount }; // Persist in storage the amount donated so far donated_so_far = donated_so_far.saturating_add(donation_amount); self.donations.insert(donor.clone(), donated_so_far); log!( "Thank you {} for donating {}! You donated a total of {}", donor.clone(), donation_amount, donated_so_far ); // Send the NEAR to the beneficiary Promise::new(self.beneficiary.clone()).transfer(to_transfer); // Return the total amount donated so far donated_so_far.to_string() } pub fn get_donation_for_account(&self, account_id: AccountId) -> Donation { let amount = self .donations .get(&account_id) .cloned() .unwrap_or(NearToken::from_near(0)) .as_yoctonear(); Donation { account_id: account_id.clone(), total_amount: U128::from(amount), } ```
### 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` ::: --- # Source: https://docs.near.org/ai/shade-agents/reference/environment-variables.md --- id: environment-variables title: Environment Variables sidebar_label: Environment Variables description: "Learn about the required and optional environment variables as part of the Shade Agent Framework." --- Environment variables are a crucial component of the Shade Agent Framework. They configure your Shade Agent and are passed encrypted into your agent when it goes live. Note that the same agent code (same Docker Compose file) can use different environment variables in different deployments. The environment variables file must be named `.env.development.local`. ## Required Variables - **NEAR_ACCOUNT_ID** :::info Example NEAR_ACCOUNT_ID=example-account.testnet ::: This is the NEAR account ID used to create the agent contract's account when running the Shade Agent CLI and is used to automatically fund the agent's account on boot. You should ensure this account remains funded as you continue to deploy additional agents. You can create a NEAR account by using the NEAR CLI. Install the NEAR CLI: ```bash curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh ``` Create an account: ```bash export ACCOUNT_ID=example-name.testnet near account create-account sponsor-by-faucet-service $ACCOUNT_ID autogenerate-new-keypair save-to-keychain network-config testnet create ``` Replace `example-name.testnet` with a unique account ID. - **NEAR_SEED_PHRASE** :::info Example NEAR_SEED_PHRASE="book chapter unknown knife strange inherit amazing artist mixture loan rotate lyrics" ::: This is the seed phrase for the NEAR_ACCOUNT_ID. When creating an account with the above command, the seed phrase will be printed to the terminal. - **NEXT_PUBLIC_contractId** :::info Examples NEXT_PUBLIC_contractId=ac-proxy.example-account.testnet NEXT_PUBLIC_contractId=ac-sandbox.example-account.testnet ::: This is the NEAR account ID where the agent contract will be deployed when running the Shade Agent CLI. The account is automatically created when you run the Shade Agent CLI. This account must be your NEAR_ACCOUNT_ID prefixed with either `ac-proxy.` or `ac-sandbox.`, which determines whether the deployment is local or to a TEE, respectively. - **API_CODEHASH** The API_CODEHASH defines the code hash of the Shade Agent API. You only need to update this when a new API version is released. The [Quickstart Template](https://github.com/NearDeFi/shade-agent-template/blob/main/.env.development.local.example#L9) always includes the most up-to-date API code hash. - **APP_CODEHASH** The APP_CODEHASH defines the code hash of your agent. You don't need to edit this as it will be automatically updated when running the Shade Agent CLI in production. - **DOCKER_TAG** :::info Example DOCKER_TAG=username/my-app ::: The DOCKER_TAG specifies the location of your app image on Docker Hub. Edit this tag to match your Docker username in the first part, and choose any name you'd like for the second part. The CLI will automatically update the Docker tag in your docker-compose.yaml file when it runs in production. - **PHALA_API_KEY** :::info Example PHALA_API_KEY=phak_tIhrDY0mXJMgmLLMEMoM6yBxOsjfVM-sTmXmjOF4Fks ::: The PHALA_API_KEY is used to upload your agent to Phala Cloud when running the Shade Agent CLI. You can get a key [here](https://cloud.phala.network/dashboard/tokens). ## Optional Environment Variables - **API_PORT** :::info Example API_PORT=4000 ::: The API_PORT defines which port number the Shade Agent API is exposed on. The API by default is hosted on port 3140. ## Your Own Variables You should also set any additional environment variables your agent may need in the `.env.development.local` file. Remember to update your [Docker Compose](https://github.com/NearDeFi/shade-agent-template/blob/main/docker-compose.yaml#L21) file to pass these additional variables to your agent. --- # Source: https://docs.near.org/smart-contracts/anatomy/environment.md --- id: environment title: Environment description: "Know which account called you, how much gas and tokens were attached, and more." --- The environment is a set of variables and functions that are available to your smart contract when it is executed. It provides information such as who called the method, how much money was attached to the call, and how many computational resources are available. Every method execution has an environment associated with information such as: 1. Who called the method 2. How much money is attached to the call 3. How many computational resources are available 4. The current timestamp 5. Helper functions for Public Key derivation, for example --- ## Environment Variables | Variable Name | SDK Variable | Description | |------------------------|---------------------------------|--------------------------------------------------------------------------------------| | Predecessor | `env::predecessor_account_id()` | Account ID that called this method | | Current Account | `env::current_account_id()` | Account ID of this smart contract | | Signer | `env::signer_account_id()` | Account ID that signed the transaction leading to this execution | | Attached Deposit | `env::attached_deposit()` | Amount in yoctoNEAR attached to the call by the predecessor | | Account Balance | `env::account_balance()` | Balance of this smart contract (including Attached Deposit) | | Prepaid Gas | `env::prepaid_gas()` | Amount of gas available for execution | | Timestamp | `env::block_timestamp()` | Current timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC) | | Current Epoch | `env::epoch_height()` | Current epoch in the blockchain | | Block Index | `env::block_index()` | Current block index (a.k.a. block height) | | Storage Used | `env::storage_usage()` | Current storage used by this smart contract in bytes | | Storage Byte Cost | `env::storage_byte_cost()` | Current storage cost per byte in yoctoNEAR | | Used Gas | `env::used_gas()` | Amount of gas used for execution | | Signer Public Key | `env::signer_account_pk()` | Sender Public Key | | Account Locked Balance | `env::account_locked_balance()` | Balance of this smart contract that is locked | | Variable Name | SDK Variable | Description | |------------------------|-------------------------------|--------------------------------------------------------------------------------------| | Predecessor | `near.predecessorAccountId()` | Account ID that called this method | | Current Account | `near.currentAccountId()` | Account ID of this smart contract | | Signer | `near.signerAccountId()` | Account ID that signed the transaction leading to this execution | | Attached Deposit | `near.attachedDeposit()` | Amount in yoctoNEAR attached to the call by the predecessor | | Account Balance | `near.accountBalance()` | Balance of this smart contract (including Attached Deposit) | | Prepaid Gas | `near.prepaidGas()` | Amount of gas available for execution | | Timestamp | `near.blockTimestamp()` | Current timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC) | | Current Epoch | `near.epochHeight()` | Current epoch in the blockchain | | Block Index | `near.blockIndex()` | Current block index (a.k.a. block height) | | Storage Used | `near.storageUsage()` | Current storage used by this smart contract | | Used Gas | `near.usedGas()` | Amount of gas used for execution | | Signer Public Key | `near.signerAccountPk()` | Sender Public Key | | Account Locked Balance | `near.accountLockedBalance()` | Balance of this smart contract that is locked | | Variable Name | SDK Variable | Description | |------------------------|------------------------------------|--------------------------------------------------------------------------------------| | Predecessor | `Context.predecessor_account_id()` | Account ID that called this method | | Current Account | `Context.current_account_id()` | Account ID of this smart contract | | Signer | `Context.signer_account_id()` | Account ID that signed the transaction leading to this execution | | Attached Deposit | `Context.attached_deposit()` | Amount in yoctoNEAR attached to the call by the predecessor | | Prepaid Gas | `Context.prepaid_gas()` | Amount of gas available for execution | | Used Gas | `Context.used_gas()` | Amount of gas used for execution | | Block Height | `Context.block_height()` | Current block height | | Timestamp | `Context.block_timestamp()` | Current timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC) | | Current Epoch | `Context.epoch_height()` | Current epoch in the blockchain | | Variable Name | SDK Variable | Description | |------------------------|---------------------------------|--------------------------------------------------------------------------------------| | Predecessor | `env.GetPredecessorAccountID()` | Account ID that called this method | | Current Account | `env.GetCurrentAccountId()` | Account ID of this smart contract | | Signer | `env.GetSignerAccountID()` | Account ID that signed the transaction leading to this execution | | Attached Deposit | `env.GetAttachedDeposit()` | Amount in yoctoNEAR attached to the call by the predecessor | | Account Balance | `env.GetAccountBalance()` | Balance of this smart contract (including Attached Deposit) | | Prepaid Gas | `env.GetPrepaidGas()` | Amount of gas available for execution | | Timestamp | `env.GetBlockTimeMs()` | Current timestamp (number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC) | | Current Epoch | `env.GetEpochHeight()` | Current epoch in the blockchain | | Block Index | `env.GetCurrentBlockHeight()` | Current block index (a.k.a. block height) | | Storage Used | `env.GetStorageUsage()` | Current storage used by this smart contract in bytes | | Used Gas | `env.GetUsedGas()` | Amount of gas used for execution | | Signer Public Key | `env.GetSignerAccountPK()` | Sender Public Key | | Account Locked Balance | `env.GetAccountLockedBalance()` | Balance of this smart contract that is locked | --- ## Who is Calling? Who am I? The environment gives you access to 3 important users: the `current_account`, the `predecessor`, and the `signer`. ### Current Account The `current_account` contains the address in which your contract is deployed. This is very useful to implement ownership, e.g. making a public method only callable by the contract itself. ### Predecessor and Signer The `predecessor` is the account that called the method in the contract. Meanwhile, the `signer` is the account that _signed_ the initial transaction. During a simple transaction (no [cross-contract calls](../anatomy/crosscontract.md)) the `predecessor` is the same as the `signer`. For example, if **alice.near** calls **contract.near**, from the contract's perspective, **alice.near** is both the `signer` and the `predecessor`. However, if **contract.near** creates a [cross-contract call](../anatomy/crosscontract.md), then the `predecessor` changes down the line. In the example below, when **pool.near** executes, it would see **contract.near** as the `predecessor` and **alice.near** as the `signer`. ![img](https://miro.medium.com/max/1400/1*LquSNOoRyXpITQF9ugsDpQ.png) *You can access information about the users interacting with your smart contract* :::tip In most scenarios you will **only need to know the predecessor**. However, there are situations in which the signer is very useful. For example, when adding [NFTs](../../primitives/nft/nft.md) into [this marketplace](https://github.com/near-examples/nft-tutorial/blob/7fb267b83899d1f65f1bceb71804430fab62c7a7/market-contract/src/nft_callbacks.rs#L42), the contract checks that the `signer`, i.e. the person who generated the transaction chain, is the NFT owner. ::: --- ## Balances and Attached NEAR The environment gives you access to 3 token-related parameters, all expressed in yoctoNEAR (1 Ⓝ = 1024yⓃ): ### Attached Deposit `attached_deposit` represents the amount of yoctoNEAR the predecessor attached to the call. This amount is **already deposited** in your contract's account, and is **automatically returned** to the `predecessor` if your **method panics**. :::warning If you make a [cross-contract call](../anatomy/crosscontract.md) and it panics, the funds are sent back to **your contract**. See how to handle this situation in the [callback section](../anatomy/crosscontract.md#what-happens-if-the-function-i-call-fails) ::: ### Account Balance `account_balance` represents the balance of your contract (`current_account`). It includes the `attached_deposit`, since it was deposited when the method execution started. If the contract has any locked $NEAR, it will appear in `account_locked_balance`. --- ### Storage Used `storage_used` represents the amount of [storage](../anatomy/storage.md) that is currently being used by your contract. :::tip If you want to know how much storage a structure uses, print the storage before and after storing it. ::: --- ## Telling the Time The environment exposes three different ways to tell the pass of time, each representing a different dimension of the underlying blockchain. ### Timestamp The `timestamp` attribute represents the approximated **UNIX timestamp** in **nanoseconds** at which this call was executed. It quantifies time passing in a human way, enabling us to check if a specific date has passed or not. ### Current Epoch The NEAR blockchain groups blocks in [Epochs](../../protocol/network/epoch.md). The `current_epoch` attribute measures how many epochs have passed so far. It is very useful to coordinate with other contracts that measure time in epochs, such as the [validators](../../protocol/network/validators.md). ### Block Index The `block_index` represents the index of the block in which this transaction will be added to the blockchain. --- ## Gas Your contract has a **limited number of computational resources** to use on each call. Such resources are measured in [Gas](/protocol/gas). Gas can be thought of as wall time, where 1 PetaGas (1_000 TGas) is ~1 second of compute time. Each code instruction costs a certain amount of Gas, and if you run out of it, the execution halts with the error message `Exceeded the prepaid gas`. The environment gives you access to two gas-related arguments: `prepaid_gas` and `used_gas`. ### Prepaid Gas `prepaid_gas` represents the amount of Gas the `predecessor` attached to this call. It cannot exceed the limit 300TGas (300 * 1012 Gas). ### Used Gas `used_gas` contains the amount of Gas that has been used so far. It is useful to estimate the Gas cost of running a method. :::warning During [cross-contract calls](./crosscontract.md) always make sure the callback has enough Gas to fully execute. ::: :::tip If you already [estimated the Gas](../../protocol/gas.md#estimating-costs-for-a-call) a method needs, you can ensure it never runs out of Gas by using `assert` ```rust const REQUIRED_GAS: Gas = Gas(20_000_000_000_000); // 20 TGas assert!(env::prepaid_gas() >= REQUIRED_GAS, "Please attach at least 20 TGas"); ``` ```python from near_sdk_py import call, Context @call def check_gas(required_gas=20_000_000_000_000): # 20 TGas if Context.prepaid_gas() < required_gas: raise Exception(f"Please attach at least {required_gas} gas") return "Sufficient gas attached" ``` ::: --- ## Environment Functions Besides environmental variables, the SDK also exposes some functions to perform basic cryptographic operations | Function Name | SDK method | Description | |-----------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | SHA 256 | `env::sha256(value)` | Hashes a sequence of bytes using sha256. | | Keccak 256 | `env::keccak256(value)` | Hashes a sequence of bytes using keccak256. | | Keccak 512 | `env::keccak512(value)` | Hashes a sequence of bytes using keccak512. | | SHA 256 (Array) | `env::sha256_array(value)` | Hashes the bytes using the SHA-256 hash function. This returns a 32 byte hash. | | Keccak 256 (Array) | `env::keccak256_array(value)` | Hashes the bytes using the Keccak-256 hash function. This returns a 32 byte hash. | | Keccak 512 (Array) | `env::keccak512_array(value)` | Hashes the bytes using the Keccak-512 hash function. This returns a 64 byte hash. | | RIPEMD 160 (Array) | `env::ripemd160_array(value)` | Hashes the bytes using the RIPEMD-160 hash function. This returns a 20 byte hash. | | EC Recover | `env::ecrecover(hash, signature, v, malleability_flag)` | Recovers an ECDSA signer address from a 32-byte message `hash` and a corresponding `signature` along with `v` recovery byte. Takes in an additional flag to check for malleability of the signature which is generally only ideal for transactions. Returns 64 bytes representing the public key if the recovery was successful. | | Panic String | `env::panic_str(message)` | Terminates the execution of the program with the UTF-8 encoded message. | | Log String | `env::log_str(message)` | Logs the string message. This message is stored on chain. | | Validator Stake | `env::validator_stake(account_id)` | For a given account return its current stake. If the account is not a validator, returns 0. | | Validator Total Stake | `env::validator_total_stake()` | Returns the total stake of validators in the current epoch. | | Function Name | SDK method | Description | |-----------------------|--------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | SHA 256 | `near.sha256(value)` | Hashes a sequence of bytes using sha256. | | Keccak 256 | `near.keccak256(value)` | Hashes a sequence of bytes using keccak256. | | Keccak 512 | `near.keccak512(value)` | Hashes a sequence of bytes using keccak512. | | RIPEMD 160 | `near.ripemd160(value)` | Hashes the bytes using the RIPEMD-160 hash function. | | EC Recover | `near.ecrecover(hash, sig, v, malleabilityFlag)` | Recovers an ECDSA signer address from a 32-byte message `hash` and a corresponding `signature` along with `v` recovery byte. Takes in an additional flag to check for malleability of the signature which is generally only ideal for transactions. Returns 64 bytes representing the public key if the recovery was successful. | | Log String | `near.log(msg)` | Logs the string message. This message is stored on chain. | | Validator Stake | `near.validatorStake(accountId)` | For a given account return its current stake. If the account is not a validator, returns 0. | | Validator Total Stake | `near.validatorTotalStake()` | Returns the total stake of validators in the current epoch. | | Function Name | SDK method | Description | |-----------------------|------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | SHA 256 | `near.sha256(value)` | Hashes a sequence of bytes using sha256. | | Keccak 256 | `near.keccak256(value)` | Hashes a sequence of bytes using keccak256. | | Keccak 512 | `near.keccak512(value)` | Hashes a sequence of bytes using keccak512. | | Log Info | `Log.info(message)` | Logs an informational message. This message is stored on chain. | | Log Warning | `Log.warning(message)` | Logs a warning message. This message is stored on chain. | | Log Error | `Log.error(message)` | Logs an error message. This message is stored on chain. | | Log Event | `Log.event(event_type, data)` | Logs a structured event following the NEP standard for events. This is useful for indexers and frontend applications. | | Raise Exception | `raise Exception(message)` | Terminates the execution of the function with an error message. Similar to panic in other languages. | | Function Name | SDK method | Description | |-----------------------|---------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | SHA 256 | `env.Sha256Hash(value)` | Hashes a sequence of bytes using sha256. | | Keccak 256 | `env.Keccak256Hash(value)` | Hashes a sequence of bytes using keccak256. | | Keccak 512 | `env.Keccak512Hash(value)` | Hashes a sequence of bytes using keccak512. | | SHA 256 | `env::Sha256Hash(value)` | Hashes the bytes using the SHA-256 hash function. This returns a 32 byte hash. | | RIPEMD 160 | `env::Ripemd160Hash(value)` | Hashes the bytes using the RIPEMD-160 hash function. This returns a 20 byte hash. | | EC Recover | `env.EcrecoverPubKey(hash, signature, v, malleability_flag)` | Recovers an ECDSA signer address from a 32-byte message `hash` and a corresponding `signature` along with `v` recovery byte. Takes in an additional flag to check for malleability of the signature which is generally only ideal for transactions. Returns 64 bytes representing the public key if the recovery was successful. | | Panic String | `env.PanicStr(message)` | Terminates the execution of the program with the UTF-8 encoded message. | | Log String | `env.LogString(message)` | Logs the string message. This message is stored on chain. | | Validator Stake | `env.ValidatorStakeAmount(account_id)` | For a given account return its current stake. If the account is not a validator, returns 0. | | Validator Total Stake | `env.ValidatorTotalStakeAmount()` | Returns the total stake of validators in the current epoch. | :::info In the JS SDK, `throw new Error("message")` mimics the behavior of Rust's `env::panic_str("message")`. ::: --- --- # Source: https://docs.near.org/protocol/network/epoch.md --- id: epoch title: Epoch description: "Learn about epochs in NEAR Protocol, including their duration, role in validator selection, and how they affect network operations and data retention." --- This section explains the concept of an **epoch** in the NEAR Protocol, which is a unit of time when validators of the network remain constant. It describes how epochs are measured, their duration, and their significance in the network's operation. An **epoch** is a unit of time when validators of the network remain constant. It is measured in blocks: - Both `testnet` and `mainnet` have an epoch duration of 43,200 blocks. Ideally epochs last about 12 hours, since blocks are created every second (in reality, they take slightly longer to be created). - You can view this setting by querying the **[`protocol_config`](/api/rpc/protocol#protocol-config)** RPC endpoint and searching for `epoch_length`. **Note:** Nodes garbage collect blocks after 5 epochs (~2.5 days) unless they are [archival nodes](https://near-nodes.io/intro/node-types#archival-node). **Example:** ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "EXPERIMENTAL_protocol_config", "params": { "finality": "final" } } ``` ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=EXPERIMENTAL_protocol_config \ params:='{ "finality": "final" }' ``` **Example Response:** ```json { "jsonrpc": "2.0", "result": { "protocol_version": 44, "genesis_time": "2020-07-21T16:55:51.591948Z", "chain_id": "mainnet", "genesis_height": 9820210, "num_block_producer_seats": 100, "num_block_producer_seats_per_shard": [ 100 ], "avg_hidden_validator_seats_per_shard": [ 0 ], "dynamic_resharding": false, "protocol_upgrade_stake_threshold": [ 4, 5 ], "epoch_length": 43200, "gas_limit": 1000000000000000, "min_gas_price": "1000000000", "max_gas_price": "10000000000000000000000", "block_producer_kickout_threshold": 90, "chunk_producer_kickout_threshold": 90, // ---- snip ---- } ``` You can learn more about how epochs are used to manage network validation in the [Validator FAQ](https://github.com/near/wiki/blob/master/Archive/validators/faq.md#what-is-an-epoch). :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/integrations/errors/error-implementation.md --- id: error-implementation title: Source Code Survey sidebar_label: Source Code Survey description: "Learn how to properly implement error handling in NEAR protocol applications, including best practices for catching and managing errors." --- This page provides a very high level, sometimes "pseudocode", view of error types and related messages as implemented by the NEAR platform. Errors raised by the NEAR platform are implemented in the following locations in `nearcore`: - [nearcore/core/primitives/src/errors.rs](https://github.com/near/nearcore/blob/master/core/primitives/src/errors.rs) - [nearcore/runtime/near-vm-errors/src/lib.rs](https://github.com/near/nearcore/blob/master/runtime/near-vm-errors/src/lib.rs) --- ## RuntimeError and subtypes {#runtimeerror-and-subtypes} ### RuntimeError {#runtimeerror} #### Definition {#definition} ```rust /// Error returned from `Runtime::apply` pub enum RuntimeError { /// An unexpected integer overflow occurred. The likely issue is an invalid state or the transition. UnexpectedIntegerOverflow, /// An error happened during TX verification and account charging. It's likely the chunk is invalid. /// and should be challenged. InvalidTxError(InvalidTxError), /// Unexpected error which is typically related to the node storage corruption.account /// That it's possible the input state is invalid or malicious. StorageError(StorageError), /// An error happens if `check_balance` fails, which is likely an indication of an invalid state. BalanceMismatchError(BalanceMismatchError), } ``` #### Error Messages {#error-messages} - see below: `InvalidTxError`, `StorageError` and `BalanceMismatchError` ### InvalidTxError {#invalidtxerror} #### Definition {#definition-1} ```rust /// An error happened during TX execution pub enum InvalidTxError { /// Happens if a wrong AccessKey used or AccessKey has not enough permissions InvalidAccessKeyError(InvalidAccessKeyError), /// TX signer_id is not in a valid format or not satisfy requirements see `near_core::primitives::utils::is_valid_account_id` InvalidSignerId { signer_id: AccountId }, /// TX signer_id is not found in a storage SignerDoesNotExist { signer_id: AccountId }, /// Transaction nonce must be account[access_key].nonce + 1 InvalidNonce { tx_nonce: Nonce, ak_nonce: Nonce }, /// TX receiver_id is not in a valid format or not satisfy requirements see `near_core::primitives::utils::is_valid_account_id` InvalidReceiverId { receiver_id: AccountId }, /// TX signature is not valid InvalidSignature, /// Account does not have enough balance to cover TX cost NotEnoughBalance { signer_id: AccountId, balance: NearToken, cost: NearToken, }, /// Signer account rent is unpaid RentUnpaid { /// An account which is required to pay the rent signer_id: AccountId, /// Required balance to cover the state rent amount: NearToken, }, /// An integer overflow occurred during transaction cost estimation. CostOverflow, /// Transaction parent block hash doesn't belong to the current chain InvalidChain, /// Transaction has expired Expired, /// An error occurred while validating actions of a Transaction. ActionsValidation(ActionsValidationError), } ``` #### Error Messages {#error-messages-1} ```rust InvalidTxError::InvalidSignerId { signer_id } "Invalid signer account ID {:?} according to requirements" InvalidTxError::SignerDoesNotExist { signer_id } "Signer {:?} does not exist" InvalidTxError::InvalidAccessKeyError(access_key_error) InvalidTxError::InvalidNonce { tx_nonce, ak_nonce } "Transaction nonce {} must be larger than nonce of the used access key {}" InvalidTxError::InvalidReceiverId { receiver_id } "Invalid receiver account ID {:?} according to requirements" InvalidTxError::InvalidSignature "Transaction is not signed with the given public key" InvalidTxError::NotEnoughBalance { signer_id, balance, cost } "Sender {:?} does not have enough balance {} for operation costing {}" InvalidTxError::RentUnpaid { signer_id, amount } "Failed to execute, because the account {:?} wouldn't have enough to pay required rent {}" InvalidTxError::CostOverflow "Transaction gas or balance cost is too high" InvalidTxError::InvalidChain "Transaction parent block hash doesn't belong to the current chain" InvalidTxError::Expired "Transaction has expired" InvalidTxError::ActionsValidation(error) "Transaction actions validation error: {}" ``` ### StorageError {#storageerror} #### Definition {#definition-2} ```rust pub enum StorageError { /// Key-value db internal failure StorageInternalError, /// Storage is PartialStorage and requested a missing trie node TrieNodeMissing, /// Either invalid state or key-value db is corrupted. /// For PartialStorage it cannot be corrupted. /// Error message is unreliable and for debugging purposes only. It's also probably ok to /// panic in every place that produces this error. /// We can check if db is corrupted by verifying everything in the state trie. StorageInconsistentState(String), } ``` ### BalanceMismatchError {#balancemismatcherror} #### Definition {#definition-3} ```rust /// Happens when the input balance doesn't match the output balance in Runtime apply. pub struct BalanceMismatchError { // Input balances pub incoming_validator_rewards: NearToken, pub initial_accounts_balance: NearToken, pub incoming_receipts_balance: NearToken, pub processed_delayed_receipts_balance: NearToken, pub initial_postponed_receipts_balance: NearToken, // Output balances pub final_accounts_balance: NearToken, pub outgoing_receipts_balance: NearToken, pub new_delayed_receipts_balance: NearToken, pub final_postponed_receipts_balance: NearToken, pub total_rent_paid: NearToken, pub total_validator_reward: NearToken, pub total_balance_burnt: NearToken, pub total_balance_slashed: NearToken, } ``` #### Error Messages {#error-messages-2} ```rust "Balance Mismatch Error. The input balance {} doesn't match output balance {}\n\ Inputs:\n\ \tIncoming validator rewards sum: {}\n\ \tInitial accounts balance sum: {}\n\ \tIncoming receipts balance sum: {}\n\ \tProcessed delayed receipts balance sum: {}\n\ \tInitial postponed receipts balance sum: {}\n\ Outputs:\n\ \tFinal accounts balance sum: {}\n\ \tOutgoing receipts balance sum: {}\n\ \tNew delayed receipts balance sum: {}\n\ \tFinal postponed receipts balance sum: {}\n\ \tTotal rent paid: {}\n\ \tTotal validators reward: {}\n\ \tTotal balance burnt: {}\n\ \tTotal balance slashed: {}", ``` ### InvalidAccessKeyError {#invalidaccesskeyerror} #### Definition {#definition-4} ```rust pub enum InvalidAccessKeyError { /// The access key identified by the `public_key` doesn't exist for the account AccessKeyNotFound { account_id: AccountId, public_key: PublicKey }, /// Transaction `receiver_id` doesn't match the access key receiver_id ReceiverMismatch { tx_receiver: AccountId, ak_receiver: AccountId }, /// Transaction method name isn't allowed by the access key MethodNameMismatch { method_name: String }, /// Transaction requires a full permission access key. RequiresFullAccess, /// Access Key does not have enough allowance to cover transaction cost NotEnoughAllowance { account_id: AccountId, public_key: PublicKey, allowance: NearToken, cost: NearToken, }, /// Having a deposit with a function call action is not allowed with a function call access key. DepositWithFunctionCall, } ``` #### Error Messages {#error-messages-3} ```rust InvalidAccessKeyError::AccessKeyNotFound { account_id, public_key } "Signer {:?} doesn't have access key with the given public_key {}" InvalidAccessKeyError::ReceiverMismatch { tx_receiver, ak_receiver } "Transaction receiver_id {:?} doesn't match the access key receiver_id {:?}" InvalidAccessKeyError::MethodNameMismatch { method_name } "Transaction method name {:?} isn't allowed by the access key" InvalidAccessKeyError::RequiresFullAccess "The transaction contains more then one action, but it was signed \ with an access key which allows transaction to apply only one specific action. \ To apply more then one actions TX must be signed with a full access key" InvalidAccessKeyError::NotEnoughAllowance { account_id, public_key, allowance, cost } "Access Key {:?}:{} does not have enough balance {} for transaction costing {}" InvalidAccessKeyError::DepositWithFunctionCall "Having a deposit with a function call action is not allowed with a function call access key." ``` ### ActionsValidationError {#actionsvalidationerror} #### Definition {#definition-5} ```rust /// Describes the error for validating a list of actions. pub enum ActionsValidationError { /// The total prepaid gas (for all given actions) exceeded the limit. TotalPrepaidGasExceeded { total_prepaid_gas: Gas, limit: Gas }, /// The number of actions exceeded the given limit. TotalNumberOfActionsExceeded { total_number_of_actions: u64, limit: u64 }, /// The total number of bytes of the method names exceeded the limit in a Add Key action. AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes: u64, limit: u64 }, /// The length of some method name exceeded the limit in a Add Key action. AddKeyMethodNameLengthExceeded { length: u64, limit: u64 }, /// Integer overflow during a compute. IntegerOverflow, /// Invalid account ID. InvalidAccountId { account_id: AccountId }, /// The size of the contract code exceeded the limit in a DeployContract action. ContractSizeExceeded { size: u64, limit: u64 }, /// The length of the method name exceeded the limit in a Function Call action. FunctionCallMethodNameLengthExceeded { length: u64, limit: u64 }, /// The length of the arguments exceeded the limit in a Function Call action. FunctionCallArgumentsLengthExceeded { length: u64, limit: u64 }, } ``` #### Error Messages {#error-messages-4} ```rust ActionsValidationError::TotalPrepaidGasExceeded { total_prepaid_gas, limit } "The total prepaid gas {} exceeds the limit {}" ActionsValidationError::TotalNumberOfActionsExceeded {total_number_of_actions, limit } "The total number of actions {} exceeds the limit {}" ActionsValidationError::AddKeyMethodNamesNumberOfBytesExceeded { total_number_of_bytes, limit } "The total number of bytes in allowed method names {} exceeds the maximum allowed number {} in a AddKey action" ActionsValidationError::AddKeyMethodNameLengthExceeded { length, limit } "The length of some method name {} exceeds the maximum allowed length {} in a AddKey action" ActionsValidationError::IntegerOverflow "Integer overflow during a compute" ActionsValidationError::InvalidAccountId { account_id } "Invalid account ID `{}`" ActionsValidationError::ContractSizeExceeded { size, limit } "The length of the contract size {} exceeds the maximum allowed size {} in a DeployContract action" ActionsValidationError::FunctionCallMethodNameLengthExceeded { length, limit } "The length of the method name {} exceeds the maximum allowed length {} in a FunctionCall action" ActionsValidationError::FunctionCallArgumentsLengthExceeded { length, limit } "The length of the arguments {} exceeds the maximum allowed length {} in a FunctionCall action" ``` ## TxExecutionError and subtypes {#txexecutionerror-and-subtypes} ### TxExecutionError {#txexecutionerror} #### Definition {#definition-6} ```rust /// Error returned in the ExecutionOutcome in case of failure pub enum TxExecutionError { /// An error happened during Acton execution ActionError(ActionError), /// An error happened during Transaction execution InvalidTxError(InvalidTxError), } ``` ### ActionError {#actionerror} #### Definition {#definition-7} ```rust ActionError pub struct ActionError { /// Index of the failed action in the transaction. /// Action index is not defined if ActionError.kind is `ActionErrorKind::RentUnpaid` pub index: Option, /// The kind of ActionError happened pub kind: ActionErrorKind, } ``` ### ActionErrorKind {#actionerrorkind} #### Definition {#definition-8} ```rust pub enum ActionErrorKind { /// Happens when CreateAccount action tries to create an account with account_id which is already exists in the storage AccountAlreadyExists { account_id: AccountId }, /// Happens when TX receiver_id doesn't exist (but action is not Action::CreateAccount) AccountDoesNotExist { account_id: AccountId }, /// A newly created account must be under a namespace of the creator account CreateAccountNotAllowed { account_id: AccountId, predecessor_id: AccountId }, /// Administrative actions like `DeployContract`, `Stake`, `AddKey`, `DeleteKey`. can be proceed only if sender=receiver /// or the first TX action is a `CreateAccount` action ActorNoPermission { account_id: AccountId, actor_id: AccountId }, /// Account tries to remove an access key that doesn't exist DeleteKeyDoesNotExist { account_id: AccountId, public_key: PublicKey }, /// The public key is already used for an existing access key AddKeyAlreadyExists { account_id: AccountId, public_key: PublicKey }, /// Account is staking and can not be deleted DeleteAccountStaking { account_id: AccountId }, /// Foreign sender (sender=!receiver) can delete an account only if a target account hasn't enough tokens to pay rent DeleteAccountHasRent { account_id: AccountId, balance: NearToken, }, /// ActionReceipt can't be completed, because the remaining balance will not be enough to pay rent. RentUnpaid { /// An account which is required to pay the rent account_id: AccountId, /// Rent due to pay. amount: NearToken, }, /// Account is not yet staked, but tries to unstake TriesToUnstake { account_id: AccountId }, /// The account doesn't have enough balance to increase the stake. TriesToStake { account_id: AccountId, stake: NearToken, locked: NearToken, balance: NearToken, }, /// An error occurred during a `FunctionCall` Action. FunctionCallError(FunctionCallError), /// Error occurs when a new `ActionReceipt` created by the `FunctionCall` action fails /// receipt validation. NewReceiptValidationError(ReceiptValidationError), } ``` #### Error Messages {#error-messages-5} ```rust ActionErrorKind::AccountAlreadyExists { account_id } "Can't create a new account {:?}, because it already exists" ActionErrorKind::AccountDoesNotExist { account_id } "Can't complete the action because account {:?} doesn't exist" ActionErrorKind::ActorNoPermission { actor_id, account_id } "Actor {:?} doesn't have permission to account {:?} to complete the action" ActionErrorKind::RentUnpaid { account_id, amount } "The account {} wouldn't have enough balance to pay required rent {}" ActionErrorKind::TriesToUnstake { account_id } "Account {:?} is not yet staked, but tries to unstake" ActionErrorKind::TriesToStake { account_id, stake, locked, balance } "Account {:?} tries to stake {}, but has staked {} and only has {}" ActionErrorKind::CreateAccountNotAllowed { account_id, predecessor_id } "The new account_id {:?} can't be created by {:?}" ActionErrorKind::DeleteKeyDoesNotExist { account_id, .. } "Account {:?} tries to remove an access key that doesn't exist" ActionErrorKind::AddKeyAlreadyExists { public_key, .. } "The public key {:?} is already used for an existing access key" ActionErrorKind::DeleteAccountStaking { account_id } "Account {:?} is staking and can not be deleted" ActionErrorKind::DeleteAccountHasRent { account_id, balance } "Account {:?} can't be deleted. It has {}, which is enough to cover the rent" ActionErrorKind::FunctionCallError(s) ActionErrorKind::NewReceiptValidationError(e) "An new action receipt created during a FunctionCall is not valid: {}" ``` ### ReceiptValidationError {#receiptvalidationerror} #### Definition {#definition-9} ```rust /// Describes the error for validating a receipt. pub enum ReceiptValidationError { /// The `predecessor_id` of a Receipt is not valid. InvalidPredecessorId { account_id: AccountId }, /// The `receiver_id` of a Receipt is not valid. InvalidReceiverId { account_id: AccountId }, /// The `signer_id` of an ActionReceipt is not valid. InvalidSignerId { account_id: AccountId }, /// The `receiver_id` of a DataReceiver within an ActionReceipt is not valid. InvalidDataReceiverId { account_id: AccountId }, /// The length of the returned data exceeded the limit in a DataReceipt. ReturnedValueLengthExceeded { length: u64, limit: u64 }, /// The number of input data dependencies exceeds the limit in an ActionReceipt. NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 }, /// An error occurred while validating actions of an ActionReceipt. ActionsValidation(ActionsValidationError), /// Receipt is bigger than the limit. /// ReceiptSizeExceeded means that there was a receipt above the size limit (currently 4MiB). /// NEAR will refuse to execute receipts that are above the size limit. /// The most likely source of such receipts would be cross-contract calls with a lot of large actions /// (contract deployment, function call with large args, etc). /// This error means that the user has to adjust their contract to generate smaller receipts. ReceiptSizeExceeded { size: u64, limit: u64 }, } ``` #### Error Messages {#error-messages-6} ```rust ReceiptValidationError::InvalidPredecessorId { account_id } "The predecessor_id `{}` of a Receipt is not valid." ReceiptValidationError::InvalidReceiverId { account_id } "The receiver_id `{}` of a Receipt is not valid." ReceiptValidationError::InvalidSignerId { account_id } "The signer_id `{}` of an ActionReceipt is not valid." ReceiptValidationError::InvalidDataReceiverId { account_id } "The receiver_id `{}` of a DataReceiver within an ActionReceipt is not valid." ReceiptValidationError::ReturnedValueLengthExceeded { length, limit } "The length of the returned data {} exceeded the limit {} in a DataReceipt" ReceiptValidationError::NumberInputDataDependenciesExceeded { number_of_input_data_dependencies, limit } "The number of input data dependencies {} exceeded the limit {} in an ActionReceipt" ReceiptValidationError::ActionsValidation(e) ReceiptValidationError::ReceiptSizeExceeded { size, limit } "The size of the receipt exceeded the limit: {} > {}", ``` ## VMError and subtypes {#vmerror-and-subtypes} ### VMError {#vmerror} #### Definition {#definition-10} ```rust pub enum VMError { FunctionCallError(FunctionCallError), /// Serialized external error from External trait implementation. ExternalError(Vec), /// An error that is caused by an operation on an inconsistent state. /// E.g. an integer overflow by using a value from the given context. InconsistentStateError(InconsistentStateError), } ``` #### Error Messages {#error-messages-7} ```rust VMError::ExternalError "Serialized ExternalError" ``` ### FunctionCallError {#functioncallerror} #### Definition {#definition-11} ```rust pub enum FunctionCallError { CompilationError(CompilationError), LinkError { msg: String }, MethodResolveError(MethodResolveError), WasmTrap { msg: String }, HostError(HostError), } ``` #### Error Messages {#error-messages-8} ```rust FunctionCallError::WasmTrap "WebAssembly trap: {}" ``` ### MethodResolveError {#methodresolveerror} #### Definition {#definition-12} ```rust pub enum MethodResolveError { MethodEmptyName, MethodUTF8Error, MethodNotFound, MethodInvalidSignature, } ``` ### CompilationError {#compilationerror} #### Definition {#definition-13} ```rust pub enum CompilationError { CodeDoesNotExist { account_id: String }, PrepareError(PrepareError), WasmerCompileError { msg: String }, } ``` #### Error Messages {#error-messages-9} ```rust CompilationError::CodeDoesNotExist "cannot find contract code for account {}" CompilationError::PrepareError(p) "PrepareError: {}" CompilationError::WasmerCompileError "Wasmer compilation error: {}" ``` ### PrepareError {#prepareerror} #### Definition {#definition-14} ```rust /// Error that can occur while preparing or executing Wasm smart-contract. pub enum PrepareError { /// Error happened while serializing the module. Serialization, /// Error happened while deserializing the module. Deserialization, /// Internal memory declaration has been found in the module. InternalMemoryDeclared, /// Gas instrumentation failed. /// /// This most likely indicates the module isn't valid. GasInstrumentation, /// Stack instrumentation failed. /// /// This most likely indicates the module isn't valid. StackHeightInstrumentation, /// Error happened during instantiation. /// /// This might indicate that `start` function trapped, or module isn't /// instantiable and/or unlinkable. Instantiate, /// Error creating memory. Memory, } ``` #### Error Messages {#error-messages-10} ```rust Serialization "Error happened while serializing the module." Deserialization "Error happened while deserializing the module." InternalMemoryDeclared "Internal memory declaration has been found in the module." GasInstrumentation "Gas instrumentation failed." StackHeightInstrumentation "Stack instrumentation failed." Instantiate "Error happened during instantiation." Memory "Error creating memory" ``` ### HostError {#hosterror} #### Definition {#definition-15} ```rust pub enum HostError { /// String encoding is bad UTF-16 sequence BadUTF16, /// String encoding is bad UTF-8 sequence BadUTF8, /// Exceeded the prepaid gas GasExceeded, /// Exceeded the maximum amount of gas allowed to burn per contract GasLimitExceeded, /// Exceeded the account balance BalanceExceeded, /// Tried to call an empty method name EmptyMethodName, /// Smart contract panicked GuestPanic { panic_msg: String }, /// IntegerOverflow happened during a contract execution IntegerOverflow, /// `promise_idx` does not correspond to existing promises InvalidPromiseIndex { promise_idx: u64 }, /// Actions can only be appended to non-joint promise. CannotAppendActionToJointPromise, /// Returning joint promise is currently prohibited CannotReturnJointPromise, /// Accessed invalid promise result index InvalidPromiseResultIndex { result_idx: u64 }, /// Accessed invalid register id InvalidRegisterId { register_id: u64 }, /// Iterator `iterator_index` was invalidated after its creation by performing a mutable operation on trie IteratorWasInvalidated { iterator_index: u64 }, /// Accessed memory outside the bounds MemoryAccessViolation, /// VM Logic returned an invalid receipt index InvalidReceiptIndex { receipt_index: u64 }, /// Iterator index `iterator_index` does not exist InvalidIteratorIndex { iterator_index: u64 }, /// VM Logic returned an invalid account id InvalidAccountId, /// VM Logic returned an invalid method name InvalidMethodName, /// VM Logic provided an invalid public key InvalidPublicKey, /// `method_name` is not allowed in view calls ProhibitedInView { method_name: String }, /// The total number of logs will exceed the limit. NumberOfLogsExceeded { limit: u64 }, /// The storage key length exceeded the limit. KeyLengthExceeded { length: u64, limit: u64 }, /// The storage value length exceeded the limit. ValueLengthExceeded { length: u64, limit: u64 }, /// The total log length exceeded the limit. TotalLogLengthExceeded { length: u64, limit: u64 }, /// The maximum number of promises within a FunctionCall exceeded the limit. NumberPromisesExceeded { number_of_promises: u64, limit: u64 }, /// The maximum number of input data dependencies exceeded the limit. NumberInputDataDependenciesExceeded { number_of_input_data_dependencies: u64, limit: u64 }, /// The returned value length exceeded the limit. ReturnedValueLengthExceeded { length: u64, limit: u64 }, /// The contract size for DeployContract action exceeded the limit. ContractSizeExceeded { size: u64, limit: u64 }, } ``` #### Error Messages {#error-messages-11} ```rust BadUTF8 "String encoding is bad UTF-8 sequence." BadUTF16 "String encoding is bad UTF-16 sequence." GasExceeded "Exceeded the prepaid gas." GasLimitExceeded "Exceeded the maximum amount of gas allowed to burn per contract." BalanceExceeded "Exceeded the account balance." EmptyMethodName "Tried to call an empty method name." GuestPanic { panic_msg } "Smart contract panicked: {}" IntegerOverflow "Integer overflow." InvalidIteratorIndex { iterator_index } "Iterator index {:?} does not exist" InvalidPromiseIndex { promise_idx } "{:?} does not correspond to existing promises" CannotAppendActionToJointPromise "Actions can only be appended to non-joint promise." CannotReturnJointPromise "Returning joint promise is currently prohibited." InvalidPromiseResultIndex { result_idx } "Accessed invalid promise result index: {:?}" InvalidRegisterId { register_id } "Accessed invalid register id: {:?}" IteratorWasInvalidated { iterator_index } "Iterator {:?} was invalidated after its creation by performing a mutable operation on trie" MemoryAccessViolation "Accessed memory outside the bounds." InvalidReceiptIndex { receipt_index } "VM Logic returned an invalid receipt index: {:?}" InvalidAccountId "VM Logic returned an invalid account id" InvalidMethodName "VM Logic returned an invalid method name" InvalidPublicKey "VM Logic provided an invalid public key" ProhibitedInView { method_name } "{} is not allowed in view calls" NumberOfLogsExceeded { limit } "The number of logs will exceed the limit {}" KeyLengthExceeded { length, limit } "The length of a storage key {} exceeds the limit {}" ValueLengthExceeded { length, limit } "The length of a storage value {} exceeds the limit {}" TotalLogLengthExceeded{ length, limit } "The length of a log message {} exceeds the limit {}" NumberPromisesExceeded { number_of_promises, limit } "The number of promises within a FunctionCall {} exceeds the limit {}" NumberInputDataDependenciesExceeded { number_of_input_data_dependencies, limit } "The number of input data dependencies {} exceeds the limit {}" ReturnedValueLengthExceeded { length, limit } "The length of a returned value {} exceeds the limit {}" ContractSizeExceeded { size, limit } "The size of a contract code in DeployContract action {} exceeds the limit {}" ``` ### VMLogicError {#vmlogicerror} #### Definition {#definition-16} ```rust pub enum VMLogicError { HostError(HostError), /// Serialized external error from External trait implementation. ExternalError(Vec), /// An error that is caused by an operation on an inconsistent state. InconsistentStateError(InconsistentStateError), } ``` ### InconsistentStateError {#inconsistentstateerror} #### Definition {#definition-17} ```rust pub enum InconsistentStateError { /// Math operation with a value from the state resulted in a integer overflow. IntegerOverflow, } ``` #### Error Messages {#error-messages-12} ```rust InconsistentStateError::IntegerOverflow "Math operation with a value from the state resulted in a integer overflow." ``` ## RPC interface {#rpc-interface} - error name - error subtype(s) - error properties ### Error Schema {#error-schema} ```json { "schema": { "BadUTF16": { "name": "BadUTF16", "subtypes": [], "props": {} }, "BadUTF8": { "name": "BadUTF8", "subtypes": [], "props": {} }, "BalanceExceeded": { "name": "BalanceExceeded", "subtypes": [], "props": {} }, "CannotAppendActionToJointPromise": { "name": "CannotAppendActionToJointPromise", "subtypes": [], "props": {} }, "CannotReturnJointPromise": { "name": "CannotReturnJointPromise", "subtypes": [], "props": {} }, "CodeDoesNotExist": { "name": "CodeDoesNotExist", "subtypes": [], "props": { "account_id": "" } }, "CompilationError": { "name": "CompilationError", "subtypes": [ "CodeDoesNotExist", "PrepareError", "WasmerCompileError" ], "props": {} }, "ContractSizeExceeded": { "name": "ContractSizeExceeded", "subtypes": [], "props": { "limit": "", "size": "" } }, "Deserialization": { "name": "Deserialization", "subtypes": [], "props": {} }, "EmptyMethodName": { "name": "EmptyMethodName", "subtypes": [], "props": {} }, "FunctionCallError": { "name": "FunctionCallError", "subtypes": [ "CompilationError", "LinkError", "MethodResolveError", "WasmTrap", "HostError" ], "props": {} }, "GasExceeded": { "name": "GasExceeded", "subtypes": [], "props": {} }, "GasInstrumentation": { "name": "GasInstrumentation", "subtypes": [], "props": {} }, "GasLimitExceeded": { "name": "GasLimitExceeded", "subtypes": [], "props": {} }, "GuestPanic": { "name": "GuestPanic", "subtypes": [], "props": { "panic_msg": "" } }, "HostError": { "name": "HostError", "subtypes": [ "BadUTF16", "BadUTF8", "GasExceeded", "GasLimitExceeded", "BalanceExceeded", "EmptyMethodName", "GuestPanic", "IntegerOverflow", "InvalidPromiseIndex", "CannotAppendActionToJointPromise", "CannotReturnJointPromise", "InvalidPromiseResultIndex", "InvalidRegisterId", "IteratorWasInvalidated", "MemoryAccessViolation", "InvalidReceiptIndex", "InvalidIteratorIndex", "InvalidAccountId", "InvalidMethodName", "InvalidPublicKey", "ProhibitedInView", "NumberOfLogsExceeded", "KeyLengthExceeded", "ValueLengthExceeded", "TotalLogLengthExceeded", "NumberPromisesExceeded", "NumberInputDataDependenciesExceeded", "ReturnedValueLengthExceeded", "ContractSizeExceeded" ], "props": {} }, "Instantiate": { "name": "Instantiate", "subtypes": [], "props": {} }, "IntegerOverflow": { "name": "IntegerOverflow", "subtypes": [], "props": {} }, "InternalMemoryDeclared": { "name": "InternalMemoryDeclared", "subtypes": [], "props": {} }, "InvalidAccountId": { "name": "InvalidAccountId", "subtypes": [], "props": {} }, "InvalidIteratorIndex": { "name": "InvalidIteratorIndex", "subtypes": [], "props": { "iterator_index": "" } }, "InvalidMethodName": { "name": "InvalidMethodName", "subtypes": [], "props": {} }, "InvalidPromiseIndex": { "name": "InvalidPromiseIndex", "subtypes": [], "props": { "promise_idx": "" } }, "InvalidPromiseResultIndex": { "name": "InvalidPromiseResultIndex", "subtypes": [], "props": { "result_idx": "" } }, "InvalidPublicKey": { "name": "InvalidPublicKey", "subtypes": [], "props": {} }, "InvalidReceiptIndex": { "name": "InvalidReceiptIndex", "subtypes": [], "props": { "receipt_index": "" } }, "InvalidRegisterId": { "name": "InvalidRegisterId", "subtypes": [], "props": { "register_id": "" } }, "IteratorWasInvalidated": { "name": "IteratorWasInvalidated", "subtypes": [], "props": { "iterator_index": "" } }, "KeyLengthExceeded": { "name": "KeyLengthExceeded", "subtypes": [], "props": { "length": "", "limit": "" } }, "LinkError": { "name": "LinkError", "subtypes": [], "props": { "msg": "" } }, "Memory": { "name": "Memory", "subtypes": [], "props": {} }, "MemoryAccessViolation": { "name": "MemoryAccessViolation", "subtypes": [], "props": {} }, "MethodEmptyName": { "name": "MethodEmptyName", "subtypes": [], "props": {} }, "MethodInvalidSignature": { "name": "MethodInvalidSignature", "subtypes": [], "props": {} }, "MethodNotFound": { "name": "MethodNotFound", "subtypes": [], "props": {} }, "MethodResolveError": { "name": "MethodResolveError", "subtypes": [ "MethodEmptyName", "MethodUTF8Error", "MethodNotFound", "MethodInvalidSignature" ], "props": {} }, "MethodUTF8Error": { "name": "MethodUTF8Error", "subtypes": [], "props": {} }, "NumberInputDataDependenciesExceeded": { "name": "NumberInputDataDependenciesExceeded", "subtypes": [], "props": { "limit": "", "number_of_input_data_dependencies": "" } }, "NumberOfLogsExceeded": { "name": "NumberOfLogsExceeded", "subtypes": [], "props": { "limit": "" } }, "NumberPromisesExceeded": { "name": "NumberPromisesExceeded", "subtypes": [], "props": { "limit": "", "number_of_promises": "" } }, "PrepareError": { "name": "PrepareError", "subtypes": [ "Serialization", "Deserialization", "InternalMemoryDeclared", "GasInstrumentation", "StackHeightInstrumentation", "Instantiate", "Memory" ], "props": {} }, "ProhibitedInView": { "name": "ProhibitedInView", "subtypes": [], "props": { "method_name": "" } }, "ReturnedValueLengthExceeded": { "name": "ReturnedValueLengthExceeded", "subtypes": [], "props": { "length": "", "limit": "" } }, "Serialization": { "name": "Serialization", "subtypes": [], "props": {} }, "StackHeightInstrumentation": { "name": "StackHeightInstrumentation", "subtypes": [], "props": {} }, "TotalLogLengthExceeded": { "name": "TotalLogLengthExceeded", "subtypes": [], "props": { "length": "", "limit": "" } }, "ValueLengthExceeded": { "name": "ValueLengthExceeded", "subtypes": [], "props": { "length": "", "limit": "" } }, "WasmTrap": { "name": "WasmTrap", "subtypes": [], "props": { "msg": "" } }, "WasmerCompileError": { "name": "WasmerCompileError", "subtypes": [], "props": { "msg": "" } }, "AccessKeyNotFound": { "name": "AccessKeyNotFound", "subtypes": [], "props": { "account_id": "", "public_key": "" } }, "AccountAlreadyExists": { "name": "AccountAlreadyExists", "subtypes": [], "props": { "account_id": "" } }, "AccountDoesNotExist": { "name": "AccountDoesNotExist", "subtypes": [], "props": { "account_id": "" } }, "ActionError": { "name": "ActionError", "subtypes": [ "AccountAlreadyExists", "AccountDoesNotExist", "CreateAccountNotAllowed", "ActorNoPermission", "DeleteKeyDoesNotExist", "AddKeyAlreadyExists", "DeleteAccountStaking", "DeleteAccountHasRent", "RentUnpaid", "TriesToUnstake", "TriesToStake", "FunctionCallError", "NewReceiptValidationError" ], "props": { "index": "" } }, "ActorNoPermission": { "name": "ActorNoPermission", "subtypes": [], "props": { "account_id": "", "actor_id": "" } }, "AddKeyAlreadyExists": { "name": "AddKeyAlreadyExists", "subtypes": [], "props": { "account_id": "", "public_key": "" } }, "BalanceMismatchError": { "name": "BalanceMismatchError", "subtypes": [], "props": { "final_accounts_balance": "", "final_postponed_receipts_balance": "", "incoming_receipts_balance": "", "incoming_validator_rewards": "", "initial_accounts_balance": "", "initial_postponed_receipts_balance": "", "new_delayed_receipts_balance": "", "outgoing_receipts_balance": "", "processed_delayed_receipts_balance": "", "total_balance_burnt": "", "total_balance_slashed": "", "total_rent_paid": "", "total_validator_reward": "" } }, "CostOverflow": { "name": "CostOverflow", "subtypes": [], "props": {} }, "CreateAccountNotAllowed": { "name": "CreateAccountNotAllowed", "subtypes": [], "props": { "account_id": "", "predecessor_id": "" } }, "DeleteAccountHasRent": { "name": "DeleteAccountHasRent", "subtypes": [], "props": { "account_id": "", "balance": "" } }, "DeleteAccountStaking": { "name": "DeleteAccountStaking", "subtypes": [], "props": { "account_id": "" } }, "DeleteKeyDoesNotExist": { "name": "DeleteKeyDoesNotExist", "subtypes": [], "props": { "account_id": "", "public_key": "" } }, "DepositWithFunctionCall": { "name": "DepositWithFunctionCall", "subtypes": [], "props": {} }, "Expired": { "name": "Expired", "subtypes": [], "props": {} }, "InvalidAccessKeyError": { "name": "InvalidAccessKeyError", "subtypes": [ "AccessKeyNotFound", "ReceiverMismatch", "MethodNameMismatch", "RequiresFullAccess", "NotEnoughAllowance", "DepositWithFunctionCall" ], "props": {} }, "InvalidChain": { "name": "InvalidChain", "subtypes": [], "props": {} }, "InvalidNonce": { "name": "InvalidNonce", "subtypes": [], "props": { "ak_nonce": "", "tx_nonce": "" } }, "InvalidReceiverId": { "name": "InvalidReceiverId", "subtypes": [], "props": { "receiver_id": "" } }, "InvalidSignature": { "name": "InvalidSignature", "subtypes": [], "props": {} }, "InvalidSignerId": { "name": "InvalidSignerId", "subtypes": [], "props": { "signer_id": "" } }, "InvalidTxError": { "name": "InvalidTxError", "subtypes": [ "InvalidAccessKeyError", "InvalidSignerId", "SignerDoesNotExist", "InvalidNonce", "InvalidReceiverId", "InvalidSignature", "NotEnoughBalance", "RentUnpaid", "CostOverflow", "InvalidChain", "Expired", "ActionsValidation" ], "props": {} }, "MethodNameMismatch": { "name": "MethodNameMismatch", "subtypes": [], "props": { "method_name": "" } }, "NotEnoughAllowance": { "name": "NotEnoughAllowance", "subtypes": [], "props": { "account_id": "", "allowance": "", "cost": "", "public_key": "" } }, "NotEnoughBalance": { "name": "NotEnoughBalance", "subtypes": [], "props": { "balance": "", "cost": "", "signer_id": "" } }, "ReceiverMismatch": { "name": "ReceiverMismatch", "subtypes": [], "props": { "ak_receiver": "", "tx_receiver": "" } }, "RentUnpaid": { "name": "RentUnpaid", "subtypes": [], "props": { "account_id": "", "amount": "" } }, "RequiresFullAccess": { "name": "RequiresFullAccess", "subtypes": [], "props": {} }, "SignerDoesNotExist": { "name": "SignerDoesNotExist", "subtypes": [], "props": { "signer_id": "" } }, "TriesToStake": { "name": "TriesToStake", "subtypes": [], "props": { "account_id": "", "balance": "", "locked": "", "stake": "" } }, "TriesToUnstake": { "name": "TriesToUnstake", "subtypes": [], "props": { "account_id": "" } }, "TxExecutionError": { "name": "TxExecutionError", "subtypes": [ "ActionError", "InvalidTxError" ], "props": {} }, "Closed": { "name": "Closed", "subtypes": [], "props": {} }, "ServerError": { "name": "ServerError", "subtypes": [ "TxExecutionError", "Timeout", "Closed" ], "props": {} }, "Timeout": { "name": "Timeout", "subtypes": [], "props": {} } } } ``` :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/api/rpc/errors.md --- id: errors title: RPC Errors description: "Understand common RPC errors and how to handle them." --- Understand common RPC errors and how to handle them. ## What Could Go Wrong? When API request fails, RPC server returns a structured error response with a limited number of well-defined error variants, so client code can exhaustively handle all the possible error cases. Our JSON-RPC errors follow [verror](https://github.com/joyent/node-verror) convention for structuring the error response: ```json { "error": { "name": , "cause": { "info": {..}, "name": }, "code": -32000, "data": String, "message": "Server error" }, "id": "dontcare", "jsonrpc": "2.0" } ``` > **Heads up** > > The fields `code`, `data`, and `message` in the structure above are considered > legacy ones and might be deprecated in the future. Please, don't rely on them. Here is the exhaustive list of the error variants that can be returned, organized by HTTP status code: ## 200 - Successful Response with Error Data These errors occur when the request was processed successfully but the requested data could not be retrieved or an operation could not be completed due to various blockchain-related conditions. **ERROR_TYPE:** `HANDLER_ERROR` ### General Errors These are general errors that can occur across different API endpoints. | ERROR_CAUSE | Reason | Solution | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | UNKNOWN_BLOCK | The requested block has not been produced yet or it has been garbage-collected (cleaned up to save space on the RPC node) | • Check that the requested block is legit
• If the block had been produced more than 5 epochs ago, try to send your request to [an archival node](https://near-nodes.io/intro/node-types#archival-node) | | INVALID_ACCOUNT | The requested `account_id` is invalid | • Provide a valid `account_id` | | UNKNOWN_ACCOUNT | The requested `account_id` has not been found while viewing since the account has not been created or has been already deleted | • Check the `account_id`
• Specify a different block or retry if you request the latest state | | UNAVAILABLE_SHARD | The node was unable to found the requested data because it does not track the shard where data is present | • Send a request to a different node which might track the shard | | NO_SYNCED_BLOCKS | The node is still syncing and the requested block is not in the database yet | • Wait until the node finish syncing
• Send a request to a different node which is synced | | NOT_SYNCED_YET | The node is still syncing and the requested block is not in the database yet | • Wait until the node finish syncing
• Send a request to a different node which is | ### Access Keys Errors specific to [Access Keys API](/api/rpc/access-keys). | ERROR_CAUSE | Reason | Solution | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | UNKNOWN_ACCESS_KEY | The requested `public_key` has not been found while viewing since the public key has not been created or has been already deleted | • Check the `public_key`
• Specify a different block or retry if you request the latest state | ### Accounts / Contracts Errors specific to [Accounts / Contracts API](/api/rpc/contracts). | ERROR_CAUSE | Reason | Solution | | ------------------------ | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | | NO_CONTRACT_CODE | The account does not have any `contract` deployed on it | • Use different account
• Specify a different block or retry if you request the latest state | | TOO_LARGE_CONTRACT_STATE | The requested contract state is too large to be returned from this node (the default limit is 50kb of state size) | • Send the request to a node with larger limits in order to view the requested state
• Spin up your own node where you can increase the limits to view state | | CONTRACT_EXECUTION_ERROR | The execution of the view function call failed (crashed, run out of the default 200 TGas limit, etc) | • Check `error.cause.info` for more details | ### Block / Chunk Errors specific to [Block / Chunk API](/api/rpc/block-chunk). | ERROR_CAUSE | Reason | Solution | | ---------------- | ------------------------------------------------------------------ | ---------------------------------------------------------------------- | | UNKNOWN_CHUNK | The requested chunk is not available or has been garbage-collected | • Check chunk ID validity
• Try an archival node for older chunks | | INVALID_SHARD_ID | The provided shard ID is invalid or doesn't exist | • Provide a valid shard ID within the network's shard range | ### Network Errors specific to [Network API](/api/rpc/network). | ERROR_CAUSE | Reason | Solution | | ------------- | ------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | UNKNOWN_EPOCH | An epoch for the provided block can't be found in a database | • Check that the requested block is legit
• If the block had been produced more than 5 epochs ago, try to send your request to an archival node
•Check that the requested block is the last block of some epoch | ### Transactions Errors specific to [Transactions API](/api/rpc/transactions). | ERROR_CAUSE | Reason | Solution | | ------------------- | ---------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | INVALID_TRANSACTION | An error happened during transaction execution | • See `error.cause.info` for details, likely a field in the transaction was invalid
•If `error.cause.info` is `ShardCongested`, resubmit the identical transaction after a delay. (Consider adding a priority fee once [NEP-541](https://github.com/near/NEPs/pull/541) is released.)
• If `error.cause.info` is `ShardStuck`, you may also resubmit the identical transaction after a delay | | UNKNOWN_RECEIPT | The receipt with the given `receipt_id` was never observed on the node | • Check the provided `receipt_id` is correct
• Send a request on a different node | ## 400 - Bad Request These errors indicate that the request was malformed or contains invalid parameters that prevent the server from processing it. **ERROR_TYPE:** `REQUEST_VALIDATION_ERROR` | ERROR_CAUSE | Reason | Solution | | ----------- | ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | PARSE_ERROR | Passed arguments can't be parsed by JSON RPC server (missing arguments, wrong format, etc.) | • Check the arguments passed and pass the correct ones
• Check `error.cause.info` for more details | ## 408 - Request Timeout These errors occur when a request takes too long to process and times out before completion. **ERROR_TYPE:** `REQUEST_VALIDATION_ERROR` ### Transaction Timeouts Errors specific to [Transactions API](/api/rpc/transactions) that occur due to timeout conditions. | ERROR_CAUSE | Reason | Solution | | ------------- | ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | TIMEOUT_ERROR | Transaction was routed, but has not been recorded on chain in 10 seconds. | • Resubmit the request with the identical transaction (in NEAR Protocol unique transactions apply exactly once, so if the previously sent transaction gets applied, this request will just return the known result, otherwise, it will route the transaction to the chain once again)
• Check that your transaction is valid
• Check that the signer account id has enough tokens to cover the transaction fees (keep in mind that some tokens on each account are locked to cover the storage cost) | ## 500 - Internal Server Error These errors indicate that something went wrong on the server side, typically due to internal issues or server overload. **ERROR_TYPE:** `INTERNAL_ERROR` | ERROR_CAUSE | Reason | Solution | | -------------- | ------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | | INTERNAL_ERROR | Something went wrong with the node itself or overloaded | • Try again later
• Send a request to a different node
• Check `error.cause.info` for more details | --- # Source: https://docs.near.org/web3-apps/concepts/eth-wallets-on-near.md --- title: EVM Wallets on NEAR id: eth-wallets-on-near description: "Understand how NEAR supports Ethereum wallets" --- Thanks to the [NEAR Wallet Selector](./web-login.md#wallet-selector), users can login into NEAR applications using [Ethereum-compatible wallets](https://ethereum.org/en/wallets/), such as MetaMask, Trust Wallet, and others. To make this possible, different components interact to translate Ethereum transactions into NEAR transactions, and vice-versa. Let's see how they work! :::info This page provides a high-level overview on how these components works, for a detailed specification please see the [NEAR Enhancement Proposal (NEP-518)](https://github.com/near/NEPs/issues/518). ::: :::tip Searching for a tutorial? Check our step-by-step tutorial on how to add Ethereum wallets support to your NEAR app using the [NEAR Wallet Selector](../tutorials/web-login/ethereum-wallets.md) ::: --- ## Components Overview Since Ethereum wallets create _ethereum transactions_ and talk with _ethereum RPCs_, three components are needed to make them work on NEAR: 1. A `Transaction Encoder` service, that encodes NEAR actions into Ethereum transactions 2. A `Translator RPC` service, that translates Ethereum RPC calls into NEAR RPC calls 3. A `Wallet Contract` that allows NEAR accounts to process EVM transactions *High-level architecture of Ethereum wallets on NEAR*
### Transaction Encoder The `Translator Encoder` - implemented [directly in the NEAR Wallet Selector](https://github.com/near/wallet-selector/blob/main/packages/ethereum-wallets/src/lib/index.ts) - takes the intent of the user (e.g. call `set_greeting` on `hello.near`) and translates it into an Ethereum transaction that the EVM wallet can sign. #### To (field) The `to` field of the Ethereum transaction is transformed following these rules: - If the `receiverId` matches `^0x[a-f0-9]{40}$` (e.g. `0xD79...314`), then the `to` field is set to the `receiverId` - Otherwise (e.g. `ana.near` or an implicit account) the `to` field is set as `keccak-256(receiverId)[12,32]` #### Data (field) The `data` field meanwhile contains the RLP-encoded NEAR actions wanted by the user, encoded as function calls on Ethereum, for example: - `FunctionCall(to: string, method: string, args: bytes, yoctoNear: uint32)` - `Transfer(to: string, yoctoNear: uint32)` - `Stake(public_key: bytes, yoctoNear: uint32)` - `AddKey(public_key: bytes, nonce: uint64, is_full_access: bool, allowance: uint256, receiver_id: string, method_names: string[])` :::info For more information on how other fields (such as `value`, `gas`, and `chainId`) are set, please refer to the [NEP-518 technical specification](https://github.com/near/NEPs/issues/518) :::
### Translator RPC The `Translator RPC` is a service deployed at `https://eth-rpc.mainnet.near.org` (for mainnet) and `https://eth-rpc.testnet.near.org` (for testnet) that translates Ethereum RPC calls into NEAR RPC calls. In other words, the `Translator RPC` simply acts as a relayer, taking the Ethereum transactions signed by the user and translating them into a function call into the `Wallet Contract` deployed in the user's account.
### Wallet Contract The `Wallet Contract` is a smart contract on NEAR that allows NEAR accounts to process EVM transactions. The contract exposes a method called `rlp_execute`, which takes as argument an RLP-encoded Ethereum transaction, verifies its signature, and executes the NEAR actions encoded in the `data` field of the transaction. Every NEAR account created through an EVM wallet has the `Wallet Contract` deployed on it. :::tip Wallet Accounts Remember that in NEAR **all accounts** can **have contracts**, and that **contracts** can perform **all the actions** that the **account can do**. ::: --- ## How it Works? Let's see how the components described above interact when a user logs in and uses an application.
### First Time Login The first time you login through your EVM wallet, the wallet selector will contact the account `ethereum-wallets.near` to create a NEAR account with the same address as your Ethereum wallet. For example, if your address on Metamask is `0xD79...314`, the NEAR account created will be `0xD79...314`. On this account, the `Wallet Contract` is deployed and a function-call key is added for the `rlp_execute` function of the contract *On your first login, a NEAR accounts with the same address as your Ethereum wallet is created, and the Wallet Contract is deployed on it*
### Using the Account Once you have logged in, you can start interacting with the application. If at some point the application needs to interact with the blockchain, Metamask will ask you to sign a transaction. Under the hood, Metamask will create an Ethereum transaction and send it to the `Translator API`, deployed at `https://eth-rpc.mainnet.near.org`. The `Translator API` will then translate the Ethereum transaction into a **function call** into the `Wallet Contract` deployed in your account. Particularly, it will call the `rlp_execute` function, passing the Ethereum transaction as an argument. The `Wallet Contract` will then execute the function call, and the application will receive the result. --- ## Resources Check the following resources to learn more about Ethereum wallets on NEAR: - [Adding EVM Wallets to your NEAR App](../tutorials/web-login/ethereum-wallets.md) - Step-by-step tutorial on how to add Ethereum wallets support to your NEAR app - [NEP-518 Technical Specification](https://github.com/near/NEPs/issues/518) - Full technical specification of how Ethereum wallets work on NEAR --- # Source: https://docs.near.org/web3-apps/tutorials/web-login/ethereum-wallets.md --- title: EVM Wallets Login id: ethereum-wallets description: "Learn how to integrate Ethereum wallets like MetaMask into your NEAR DApp using the Near Wallet Selector, Web3Modal, and wagmi libraries." --- Using the [Wallet Selector](./wallet-selector.md) it is possible to login into NEAR applications using Ethereum wallets like MetaMask, WalletConnect and many others. This tutorial will guide you to add Ethereum wallet support to your NEAR application using the [Reown](https://reown.com/appkit) library, which is widely used in the Ethereum ecosystem. :::info Learn more about Ethereum Wallets on NEAR in our [concepts page](../../concepts/eth-wallets.md) ::: --- ## Overview To integrate Metamask and other EVM wallets you will need to: 1. Add the `@near-wallet-selector/ethereum-wallets` module 2. Add the EVM libraries `wagmi` and `reown` 3. Create configurations so the Ethereum wallets can communicate with our [Translator RPC](../../concepts/eth-wallets.md#translator-rpc) 4. Create a Web3Modal and connect it to the Near Wallet Selector 5. Initialize the Ethereum Wallets We will show how we added Ethereum Wallets support to our [**Hello Near Examples**](https://github.com/near-examples/hello-near-examples/tree/main/frontend). :::tip This article was created by the AuroraLabs team, and appeared originally in the [official Aurora documentation](https://doc.aurora.dev/dev-reference/eth-wallets) ::: --- ## 1. Update Wallet Selector libraries Lets start by updating the `package.json`, adding all the necessary libraries to support Ethereum wallets.
### Wallet Selector Packages In your `package.json`, add the `@near-wallet-selector/ethereum-wallets` package, and update **all** wallet selector packages to version `8.9.13` or above: ```json title="package.json" "dependencies": { ... "@near-wallet-selector/core": "^9.5.0", // highlight-next-line "@near-wallet-selector/ethereum-wallets": "^9.5.0", "@near-wallet-selector/here-wallet": "^9.5.0", "@near-wallet-selector/modal-ui": "^9.5.0", "@near-wallet-selector/my-near-wallet": "^9.5.0", ... } ```
### Add Web3Modal libraries [Web3Modal (also known as AppKit)](https://reown.com/appkit) is a standard way to integrate multiple wallets in Ethereum community. It is based on [wagmi] hooks library for React. We will describe the React integration here, but if you are on another platform - just go [here](https://docs.reown.com/appkit/overview#get-started), and try using specific instructions suitable for you to install it. ```bash npm install @reown/appkit-adapter-wagmi @reown/appkit @wagmi/core viem ``` --- ## 2. Add Web3Modal First, let's create a new file to handle the Web3Modal (i.e. the modal shown when selecting the `Ethereum Wallets` on the `Wallet Selector`), and all the configs needed to setup the Ethereum Wallets. ``` import { reconnect } from '@wagmi/core'; import { createAppKit } from '@reown/appkit/react' import { nearTestnet } from '@reown/appkit/networks' import { WagmiAdapter } from '@reown/appkit-adapter-wagmi' const projectId = '5bb0fe33763b3bea40b8d69e4269b4ae'; export const wagmiAdapter = new WagmiAdapter({ projectId, networks: [nearTestnet], }); export const web3Modal = createAppKit({ adapters: [wagmiAdapter], projectId, networks: [nearTestnet], enableWalletConnect: true, features: { analytics: true, swaps: false, onramp: false, email: false, // Smart accounts (Safe contract) not available on NEAR Protocol, only EOA. socials: false, // Smart accounts (Safe contract) not available on NEAR Protocol, only EOA. }, coinbasePreference: "eoaOnly", // Smart accounts (Safe contract) not available on NEAR Protocol, only EOA. allWallets: "SHOW", }); // force reconnecting if the user has already signed in with an ethereum wallet // this is a workaround until `ethereum-wallets` supports the `reconnect` method if (typeof window !== "undefined") { const recentWallets = localStorage.getItem("near-wallet-selector:recentlySignedInWallets"); recentWallets && recentWallets.includes("ethereum-wallets") && reconnect(wagmiAdapter.wagmiConfig) } ```
Metadata You can pass a `metadata` object to the `walletConnect` connector. This object will be displayed in the EVM wallets, like MetaMask. ```js title="source/wallets/web3modal.js" const url = "http://localhost:3000"; const metadata = { name: "Onboard to NEAR Protocol with EVM Wallet", description: "Discover NEAR Protocol with Ethereum and NEAR wallets.", url: url, icons: [`${url}/icon.svg`], }; ``` This tracks the app requesting the connection on the WalletConnect side. See more [here](https://wagmi.sh/core/api/connectors/walletConnect#metadata).
:::tip Make sure to call `reconnect(wagmiConfig)` in your code, to persist the connection between the app and the wallet when the user refreshes the page :::
### Get `projectId` Notice that the modal uses a `projectId`, which refers to your unique project on `Reown`. Let's get the Web3Modal `projectId` for your project: 1. Go to [Cloud Reown](https://cloud.reown.com/). 2. Register there. 3. Create a project on Cloud Reown. 4. You can copy your `projectId`: ![reown_projectid](https://doc.aurora.dev/assets/images/reown_projectid-dbd1cc5521998d2f16545598ac925a5e.png) :::tip You can read more about the `projectId` and how it works [here](https://docs.reown.com/appkit/react/core/installation#cloud-configuration). ::: --- ## 4. Setup Wallet Selector The last step is to add the Ethereum Wallets selector to your Near Wallet Selector. Let's find your `setupWalletSelector` call and add `setupEthereumWallets` there: ```js showLineNumbers ``` ``` import { setupBitgetWallet } from "@near-wallet-selector/bitget-wallet"; import { setupRamperWallet } from "@near-wallet-selector/ramper-wallet"; import { setupUnityWallet } from "@near-wallet-selector/unity-wallet"; import { setupOKXWallet } from "@near-wallet-selector/okx-wallet"; import { setupCoin98Wallet } from "@near-wallet-selector/coin98-wallet"; import { setupIntearWallet } from "@near-wallet-selector/intear-wallet"; // Ethereum adapters import { wagmiAdapter, web3Modal } from "@/wallets/web3modal"; // Types import type { WalletModuleFactory } from "@near-wallet-selector/core"; const walletSelectorConfig = { network: NetworkId, modules: [ setupEthereumWallets({ wagmiConfig: wagmiAdapter.wagmiConfig, web3Modal }), setupMeteorWallet(), setupMeteorWalletApp({ contractId: HelloNearContract }), setupHotWallet(), setupLedger(), ``` --- ## 5. Use It! That is it! Just re-build your project and click on login! You should see Ethereum Wallets option in your Near Selector: ![ethwallets_popup1](https://doc.aurora.dev/assets/images/ethwallets_popup1-b113d70e3578a75f0f996aa3bcdf43e9.png) And after click to be able to choose the EVM wallet of your taste: ![ethwallets_popup2](https://doc.aurora.dev/assets/images/ethwallets_popup2-8484d037a465af5134f112fba6eef918.png) --- ## Resources 1. [Source code of the project above](https://github.com/near-examples/hello-near-examples/blob/main/frontend/) 2. [Example of the EVM account on the Near Testnet](https://testnet.nearblocks.io/address/0xe5acd26a443d2d62f6b3379c0a5b2c7ac65d9454) to see what happens in reality on-chain during the execution. 3. Details about how does it work are in [NEP-518](https://github.com/near/NEPs/issues/518) 4. [Recording of the Near Devs call](https://drive.google.com/file/d/1xGWN1yRLzFmRn1e29kbSiO2W1JsxuJH-/view?usp=sharing) with the EthWallets presentation. --- # Source: https://docs.near.org/integrations/exchange-integration.md --- id: exchange-integration title: Exchange Integration sidebar_label: Exchange Integration description: "Learn how to integrate NEAR Protocol into exchanges, including transactions, accounts, tokens, blocks, finality, archival nodes, and staking." --- This document provides an overview of how to integrate with NEAR Protocol for exchanges, including account management, token transfers, and handling implicit accounts. ## Integration Reference {#integration-reference} - [Balance Changes](/integrations/balance-changes) - [Accounts](/integrations/accounts) - [Fungible Tokens](/integrations/fungible-tokens) - [Implicit Accounts](/integrations/implicit-accounts) ### Transaction Reference Links {#transaction-reference-links} - [Basics](/protocol/transactions) - [Specifications](https://nomicon.io/RuntimeSpec/Transactions) - [Constructing Transactions](/integrations/create-transactions) ## Blocks and Finality {#blocks-and-finality} Some important pieces of information regarding blocks and finality include: - Expected block time is around 1s and expected time to finality is around 2s. The last final block can be queried by specifying `{"finality": "final"}` in the block query. For example, to get the latest final block on mainnet, one can run ```bash http post https://rpc.mainnet.near.org method=block params:='{"finality":"final"}' id=123 jsonrpc=2.0 ``` - Block height are not necessarily continuous and certain heights may be skipped if, for example, a block producer for that height is offline. For example, after a block at height 100 is produced, the block at height 101 may be skipped. When block at height 102 is produced, its previous block is the block at height 100. - Some blocks may not include new chunks if, for example, the previous chunk producer is offline. Even though in the RPC return result every block will have non-empty `chunks` field, it does not imply that there is a new chunk included in the block. The way to tell whether the chunk is included in the block is to check whether `height_included` in the chunk is the same as the height of the block. ## Running an Archival Node {#running-an-archival-node} Please refer to configuration changes required in `config.json` for archival node by referring to the documentation on [Run an Archival Node](https://near-nodes.io/archival/run-archival-node-with-nearup). ## Staking and Delegation {#staking-and-delegation} - [https://github.com/nearprotocol/stakewars](https://github.com/nearprotocol/stakewars) - [https://github.com/near/core-contracts](https://github.com/near/core-contracts) :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/tools/explorer.md --- id: explorer title: Explorer sidebar_label: Explorers description: "Explore the chain through a Web UI." --- Explorers are web applications that allow you to quickly obtain information from the blockchain through a friendly user interface. An explorer can help you to check the balance of an account, search for a transaction and its receipts, check the history of interactions in an account or contract, and view block creations in real time. --- ## NearBlocks Created by the community, [NearBlocks](https://nearblocks.io/) enables to check accounts and transactions with an interface similar to etherscan. ![NearBlocks](/assets/docs/data-infrastructure/explorers/nearblocks.png) *Main page of [NearBlocks](https://nearblocks.io/)*
## Nearscope [Nearscope](https://nearscope.net/) provides a NEAR node validator and delegator explorer. ![Nearscope](/assets/docs/data-infrastructure/explorers/nearscope.png) *Main page of [Nearscope](https://nearscope.net/)*
## DappLooker [DappLooker](https://dapplooker.com/) lets you analyze and query NEAR blockchain data, build dashboards to visualize data and share with your community. ![DappLooker](/assets/docs/data-infrastructure/explorers/dapplooker.png) *Main page of [DappLooker](https://dapplooker.com/)*
## Pikespeak [Pikespeak](https://pikespeak.ai/) provides access to real-time and historical data on the NEAR Protocol. ![Pikespeak](/assets/docs/data-infrastructure/explorers/pikespeak.png) *Main page of [Pikespeak](https://pikespeak.ai/)* ## NEARCatalog [NEARCatalog](https://nearcatalog.xyz/) provides access to trending decentralized applications (DApps) on the NEAR Protocol. ![Nearcatalog](/assets/docs/data-infrastructure/explorers/nearcatalog.png) *Main page of [NEARCatalog](https://nearcatalog.xyz/)* --- # Source: https://docs.near.org/tutorials/examples/factory.md --- id: factory title: Factory description: "Learn how a factory contract deploys other contracts on sub-accounts using a global contract ID." --- A factory is a smart contract that stores a global contract id, and automatizes deploying contracts onto new sub-accounts. We have a [**A Generic Factory**](https://github.com/near-examples/factory-rust) that deploys new contracts based on global contract id. The global contract id can be global account id or hash. You can change the global contract id by calling `update_global_contract_id` method. The factory creates sub-accounts of itself and deploys corresponding contract on them. :::info You can learn more about global contracts on NEAR [here](../../smart-contracts/global-contracts.md). ::: --- ## Overview {#generic-factory} The factory is a smart contract that: 1. Creates sub-accounts of itself and deploys its contract on them (`deploy`) 2. Can update the contract it deploys ``` #[payable] pub fn deploy(&mut self, name: String) -> Promise { // Assert enough tokens are attached to cover minimal initial deposit on created account let attached = env::attached_deposit(); let minimum_needed = self.min_deposit_amount.exact_amount_display(); assert!( attached.ge(&self.min_deposit_amount), "Attach at least {minimum_needed}" ); // Assert the sub-account is valid let current_account = env::current_account_id().to_string(); let subaccount: AccountId = format!("{name}.{current_account}").parse().unwrap(); assert!( env::is_valid_account_id(subaccount.as_bytes()), "Invalid subaccount" ); let promise = Promise::new(subaccount) .create_account() .transfer(env::attached_deposit()) .add_full_access_key(env::signer_account_pk()); match self.global_contract_id { GlobalContractId::AccountId(ref account_id) => { env::log_str(&format!( "Using global contract deployed by account: {}", account_id )); promise.use_global_contract_by_account_id(account_id.clone()) } GlobalContractId::CodeHash(ref code_hash) => { env::log_str(&format!( "Using global contract with code hash: {:?}", code_hash )); promise.use_global_contract(bs58::decode(code_hash).into_vec().unwrap()) } } } } ``` ``` pub fn update_global_contract_id(&mut self, contract_id: GlobalContractId, min_deposit: NearToken) { self.global_contract_id = contract_id; self.min_deposit_amount = min_deposit; } ``` --- ## Quickstart 1. Make sure you have installed [rust](https://www.rust-lang.org/). 2. Install the [`NEAR CLI`](/tools/near-cli#installation) 3. Install the [`cargo near`](https://github.com/near/cargo-near) extension.
### Build and Deploy the Factory You can automatically compile and deploy the contract in the NEAR testnet by running: ```bash cargo near deploy ```
### Deploy the Stored Contract Into a Sub-Account Method `deploy` will create a sub-account of the factory and deploy contract on it using stored global contract id. It also asserts that the attached deposit is at least the minimum required deposit stored in the factory. :::info Technically, there is no need to attach any deposit just to deploy a contract using global contracts. But to initialize it further, you will need some NEAR tokens to cover the storage cost. Since initiliazing method can't accept deposit, it makes sense to attach some minimum amount of tokens during creating account and deploying a contract. ::: The following command will create the `sub.`, which will have a global contract deployed on it. ```bash near contract call-function as-transaction deploy json-args '{"name": "sub"}' prepaid-gas '100.0 Tgas' attached-deposit '0.2 NEAR' sign-as network-config testnet sign-with-keychain send ``` By default, the global contract is the [Fungible Token](../../primitives/ft/ft.md#global-contracts) primitive contract `ft.globals.primitives.testnet`. To initilize the contract, you can call its `new_default_meta` method: ```bash near contract call-function as-transaction sub. new_default_meta json-args '{"owner_id": "", "total_supply": "100000000000000000000000000"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send ``` Then you can call `ft_metadata` method to verify that the contract is deployed and initialized correctly: ```bash near contract call-function as-read-only sub. ft_metadata json-args {} network-config testnet now # The response should be like this: # { # "decimals": 24, # "icon": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 288 288'%3E%3Cg id='l' data-name='l'%3E%3Cpath d='M187.58,79.81l-30.1,44.69a3.2,3.2,0,0,0,4.75,4.2L191.86,103a1.2,1.2,0,0,1,2,.91v80.46a1.2,1.2,0,0,1-2.12.77L102.18,77.93A15.35,15.35,0,0,0,90.47,72.5H87.34A15.34,15.34,0,0,0,72,87.84V201.16A15.34,15.34,0,0,0,87.34,216.5h0a15.35,15.35,0,0,0,13.08-7.31l30.1-44.69a3.2,3.2,0,0,0-4.75-4.2L96.14,186a1.2,1.2,0,0,1-2-.91V104.61a1.2,1.2,0,0,1,2.12-.77l89.55,107.23a15.35,15.35,0,0,0,11.71,5.43h3.13A15.34,15.34,0,0,0,216,201.16V87.84A15.34,15.34,0,0,0,200.66,72.5h0A15.35,15.35,0,0,0,187.58,79.81Z'/%3E%3C/g%3E%3C/svg%3E", # "name": "Example NEAR fungible token", # "reference": null, # "reference_hash": null, # "spec": "ft-1.0.0", # "symbol": "EXAMPLE" #} ```
### Update the Stored Contract `update_global_contract_id` enables to change the global contract id that the factory stores. The method is interesting because it has no declared parameters, and yet it takes an input: the new contract to store as a stream of bytes. To use it, we need to pass the contract id we want to store. It can be in form of account id or global contract hash. What is the difference between them you can read in [Global Contracts](../../smart-contracts/global-contracts.md#solution) section. ```bash near contract call-function as-transaction update_global_contract_id json-args '{"contract_id": "3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send ``` --- ## Factories - Concepts & Limitations Factories are an interesting concept, here we further explain some of their implementation aspects, as well as their limitations.
### Automatically Creating Accounts NEAR accounts can only create sub-accounts of itself, therefore, the `factory` can only create and deploy contracts on its own sub-accounts. This means that the factory: 1. **Can** create `sub.factory.testnet` and deploy a contract on it. 2. **Cannot** create sub-accounts of the `predecessor`. 3. **Can** create new accounts (e.g. `account.testnet`), but **cannot** deploy contracts on them. It is important to remember that, while `factory.testnet` can create `sub.factory.testnet`, it has no control over it after its creation.
### The Deploy Method During the creation of a sub-account we add signers public key to the new sub-account as a full access key. It means that the predecessor will be able to control the new sub-account after its creation. Also, in the tutorial, we attach deposit to the `deploy` call which goes straight to the new sub-account. When we deploy a new contract using global contracts, we need to initialize it after deployment. That tokens will cover the storage after the deployed contract is initialized. --- # Source: https://docs.near.org/integrations/faq.md --- id: faq title: Integrator FAQ sidebar_label: Integrator FAQ description: "Frequently asked questions about NEAR protocol integrations, including account management, transaction fees, and common development challenges." --- This document provides answers to frequently asked questions about integrating with the NEAR Protocol. It covers topics such as account management, validator operations, transaction processing, and more. ## Orientation ### What is a good project summary for NEAR? NEAR is a sharded, public, proof-of-stake blockchain and smart contract platform. It is built in Rust and contracts compile to WASM. It is conceptually similar to Ethereum 2.0. ### What's special about NEAR? NEAR is the blockchain for builders. If you understand the basics of web development, you can write, test and deploy scalable decentralized applications in minutes on the most developer-friendly blockchain without having to learn new tools or languages. ### Is NEAR open source? Yes. Have a look at our [GitHub organization](https://github.com/near). ### How are cryptographic functions used? We support both `secp256k1` and `ed25519` for account keys and `ed25519` for signing transactions. We currently use the `ed25519_dalek` and `sha2` libraries for crypto. ### Do you have any on-chain governance mechanisms? NEAR does not have any on-chain governance at the moment. Any changes to state or state transition function must be done through a hard fork. ### Do you have a bug-bounty program? Our plan is to have a transparent Bug Bounty program with clear guidelines for paying out to those reporting issues. Payments will likely be based on publicly available rankings provided by protocol developers based on issue severity. ### What contracts should we be aware of right now? We have developed a number of [initial contracts](https://github.com/near/core-contracts) with **ones in bold** being most mature at time of writing - **Staking Pool / Delegation contract** - **Lockup / Vesting contract** - Whitelist Contract - Staking Pool Factory - Multisig contract ### Do you have a cold wallet implementation (ie. Ledger)? https://github.com/near/near-ledger-app ## Validators ### What is the process for becoming a validator? Validation is permissionless and determined via auction. Parties who want to become a validator submit a special transaction to the chain one day ahead which indicates how many tokens they want to stake. An auction is run which determines the minimum necessary stake to get a validation seat during the next epoch and, if the amount submitted is greater than the minimum threshold, the submitter will validate at least one shard during the next epoch. ### How long does a validator remain a validator? A validator will stop being a validator for the following reasons: - Not producing enough blocks or chunks. - Not getting elected in the auction for next epoch because their stake is not large enough. - Getting slashed. Otherwise a validator will remain a validator indefinitely. Validator election happens in epochs. The [Nightshade whitepaper](/papers/Nightshade.pdf) introduces epochs this way: "the maintenance of the network is done in epochs" where an epoch is a period of time on the order of half a day. At the beginning of each epoch, some computation produces a list of validators for the _very next epoch_. The input to this computation includes all accounts that have "raised their hand to be a validator" by submitting a special transaction ([`StakeAction`](https://nomicon.io/RuntimeSpec/Actions.html#stakeaction)) expressing the commitment of some amount of tokens over the system's staking threshold, as well as validators from the previous epoch. The output of this computation is a list of the validators for the very next epoch. ### What is the penalty for misbehaving validators? Validators are not slashed for being offline but they do miss out on the rewards of validating. Validators who miss too many blocks or chunks will be removed from the validation set in the next auction and not get any reward (but, again, without slashing). Any foul play on the part of the validator that is detected by the system may result in a "slashing event" where the validator is marked as out of integrity and forfeits their stake (according to some formula of progressive severity). The slashed stake is burnt. ### What is the mechanism for delegating stake to validators? NEAR supports separate validation keys that can be used in smart contracts to delegate stake. Delegation is done via smart contract which allows for a validator to define a custom way to collect stake, manage it and split rewards. This also allows validators to provide leverage or derivatives on stake. Delegated stake will be slashed like any other stake if the node misbehaves. If a validator misbehaves the funds of the delegators are also slashed. There is no waiting period for delegators to withdraw their stake. ### Does a validator control funds that have been delegated to them? Delegation is custodial (you are transferring funds to a different account, the smart contract that implements staking pool). We provide a reference implementation being security reviewed and tested by 100 validators at time of writing. We allow validators to write and deploy new contracts but it is up to users to decide if they want to delegate. Validators can compete for delegation by choosing different logic and conditions around tax optimization, etc. Currently no slashing but will be added as we add shards into the system. At some point validators will be able to add an option to shield delegators from slashing (similar to Tezos model). ### How do we get the balance of an account after it has delegated funds? One would need to query the staking pool contract to get balance. ## Nodes ### Can a node be configured to archive all blockchain data since genesis? v Yes. Start the node using the following command: ```sh ./target/release/near run --archive ``` ### Can a node be configured to expose an RPC (ex: HTTP) interface? Yes. All nodes expose this interface by default which can be configured by setting the value of `listen_addr:port` in the node's `config.json` file. ### Can a node be gracefully terminated and restarted (using archived data on disk to continue syncing)? Yes. ### Does a node expose an interface for retrieving health telemetry in a structured format (ex: JSON) over RPC? Yes. `GET /status` and `GET /health` provide this interface. - `/status`: block height, syncing status, peer count, etc - `/health`: success/failure if node is up running & progressing ### Can a node be started using a Dockerfile without human supervision? Yes. ```sh docker run nearprotocol/nearcore:latest ``` See `nearcore/scripts/nodelib.py` for different examples of configuration. ### What is the source of truth for current block height exposed via API? - MainNet - https://nearblocks.io - `https://rpc.mainnet.near.org/status` - TestNet - https://testnet.nearblocks.io - `https://rpc.testnet.near.org/status` ### How old can the referenced block hash be before it's invalid? There is a genesis parameter which can be discovered for any network using: ```sh http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=EXPERIMENTAL_genesis_config # in the line above, testnet may be replaced with mainnet or betanet ``` It's `43200` seconds or `~12` hours. You can view the live configuration for `epoch_length` using the [`protocol_config` RPC endpoint](/api/rpc/protocol#protocol-config). In the response we find `transaction_validity_period": 86400` (and since it takes about 1 second to produce a block, this period is about 24 hrs) ## Blockchain ### How will the network will be bootstrapped? Distribution at genesis will be spread among the NEAR team, our contributors, project partners (ie. contributor, beta applications, infrastructure developers, etc.) and the NEAR foundation (with many portions of that segregated for post-MainNet distribution activity and unavailable to stake so the foundation isn’t able to control the network). There will be auctions occurring on the platform after launch which will allocate large amounts of tokens over the next 2 years. Additionally we are planning to run TestNet where any validator who participates will receive rewards in real tokens. We are planning to onboard at least 50 separate entities to be validators at launch. ### What is the network upgrade process? We are currently upgrading via restarting with a new genesis block. ### Which consensus algorithm does NEAR use? NEAR is a sharded **proof-of-stake** blockchain. _You can read more in our [Nightshade whitepaper](/papers/Nightshade.pdf)._ > _A few relevant details have been extracted here for convenience:_ > > [Since NEAR is a sharded blockchain, there are challenges that need to be overcome] including state validity and data > availability problems. _Nightshade_ is the solution NEAR Protocol is built upon that addresses these issues. > > Nightshade uses the heaviest chain consensus. Specifically when a block producer produces a block (see section 3.3), they can collect signatures from other block producers and validators attesting to the previous block. The weight of a block is then the cumulative stake of all the signers whose signatures are included in the block. The weight of a chain is the sum of the block weights. > > On top of the heaviest chain consensus we use a finality gadget that uses the attestations to finalize the blocks. To reduce the complexity of the system, we use a finality gadget that doesn’t influence the fork choice rule in any way, and instead only introduces extra slashing conditions, such that once a block is finalized by the finality gadget, a fork is impossible unless a very large percentage of the total stake is slashed. ### How does on-chain transaction finality work? Finality is deterministic, and requires at least 3 blocks as well as (2/3 +1) signatures of the current validator set. In a normal operation, we expect this to happen right at 3 blocks but it is not guaranteed. Finality will be exposed via RPC when querying block or transaction. Our definition of finality is BOTH: - Block has quorum pre-commit from the finality gadget. See details of the finality gadget [[here]](/papers/PoST.pdf) - At least 120 blocks (2-3 minutes) built on top of the block of interest. This is relevant in case of invalid state transition in some shard and provides enough time for state change challenges. In case all shards are tracked and some mechanics to pause across nodes is employed, this is not needed. We recommend exchanges track all shards. ## Accounts ### How are addresses generated? Please check out the spec here on accounts https://nomicon.io/DataStructures/Account.html. ### What is the balance record-keeping model on the NEAR platform? NEAR uses an `Account`-based model. All users and contracts are associated with at least 1 account. Each account lives on a single shard. Each account can have multiple keys for signing transactions. _You can read [more about NEAR accounts here](https://nomicon.io/DataStructures/Account.html)_ ### How are user accounts represented on-chain? Users create accounts with human-readable names (eg `alice`) which can contain multiple keypairs with individual permissions. Accounts can be atomically and securely transferred between parties as a native transaction on the network. Permissions are programmable with smart contracts as well. For example, a lock up contract is just an account with permission on the key that does not allow to transfer funds greater than those unlocked. ### Is there a minimum account balance? To limit on-chain "dust", accounts (and contracts) are charged a refundable deposit for storing data on the chain. This means that if the balance of the account does not have enough balance to cover an increased deposit for additional storage of data, storing additional data will fail. Also, any user can remove their own account and transfer left over balance to another (beneficiary) account. There will be a restoration mechanism for accounts removed (or slept) in this way implemented in the future. ### How many keys are used? An account can have arbitrarily many keys, as long as it has enough tokens for their storage. ### Which balance look-ups exist? What is required? We have an [RPC method for viewing account](/api/rpc/contracts#view-account). The [JS implementation is here](https://github.com/near/near-api-js/blob/d7f0cb87ec320b723734045a4ee9d17d94574a19/src/providers/json-rpc-provider.ts#L73). Note that in this RPC interface you can specify the finality requirement (whether to query the latest state or finalized state). For custody purposes, it is recommended not to rely on latest state but only what is finalized. ## Fees ### What is the fee structure for on-chain transactions? NEAR uses a gas-based model where prices are generally deterministically adjusted based on congestion of the network. We avoid making changes that are too large through re-sharding by changing number of available shards (and thus throughput). Accounts don’t have associated resources. Gas amount is predetermined for all transactions except function calls. For function call transactions the user (or more likely the developer) attaches the required amount of gas. If some gas is left over after the function call, it is converted back to NEAR and refunded to the original funding account. ### How do we know how much gas to add to a transaction? - See reference documentation here: https://nomicon.io/Economics/Economics.html - See API documentation for [discovering gas price via RPC here](/api/rpc/gas#gas-price). The issuer of a transaction should attach some amount of gas by taking a guess at budget which will get the transaction processed. The contract knows how much to fund different cross contract calls. Gas price is calculated and fixed per block, but may change from block to block depending on how full / busy the block is. If blocks become more than half full then gas price increases. We're also considering adding a max gas price limit. ## Transactions ### How do we follow Tx status? See related [RPC interface for fetching transaction status here](/api/rpc/transactions#transaction-status). ### How are transactions constructed and signed? Transactions are a collection of related data that is composed and cryptographically signed by the sender using their private key. The related public key is part of the transaction and used for signature verification. Only signed transactions may be sent to the network for processing. Transactions can be constructed and signed offline. Nodes are not required for signing. We are planning to add optional recent block hash to help prevent various replay attacks. See [transactions](/protocol/transactions) in the concepts section of our documentation. ### How is the hash preimage generated? Which fields does the raw transaction consist of? For a transaction, we sign the hash of the transaction. More specifically, what is signed is the `sha256` of the transaction object serialized in borsh (https://github.com/near/borsh). ### How do transactions work on the NEAR platform? A `Transaction` is made up of one or more `Action`s. An action can (currently) be one of 8 types: `CreateAccount`, `DeployContract`, `FunctionCall`, `Transfer`, `Stake`, `AddKey`, `DeleteKey` and `DeleteAccount`. Transactions are composed by a sender and then signed using the private keys of a valid NEAR account to create a `SignedTransaction`. This signed transaction is considered ready to send to the network for processing. Transactions are received via our JSON-RPC endpoint and routed to the shared where the `sender` account lives. This "home shard" for the sender account is then responsible for processing the transaction and generating related receipts to be applied across the network. Once received by the network, signed transactions are verified (using the embedded public key of the signer) and transformed into a collection of `Receipt`s, one per action. Receipts are of two types: `Action Receipt` is the most common and represents almost all actions on the network while `Data Receipt` handles the very special case of "a `FunctionCallAction` which includes a Promise". These receipts are then propagated and applied across the network according to the "home shard" rule for all affected receiver accounts. These receipts are then propagated around the network using the receiver account's "home shard" since each account lives on one and only one shard. Once located on the correct shard, receipts are pulled from a nonce-based [queue](https://nomicon.io/ChainSpec/Transactions#pool-iterator). Receipts may generate other, new receipts which in turn are propagated around the network until all receipts have been applied. If any action within a transaction fails, the entire transaction is rolled back and any unburnt fees are refunded to the proper accounts. For more detail, see specs on [`Transactions`](https://nomicon.io/RuntimeSpec/Transactions), [`Actions`](https://nomicon.io/RuntimeSpec/Actions.html), [`Receipts`](https://nomicon.io/RuntimeSpec/Receipts) ### How does NEAR serialize transactions? We use a simple binary serialization format that's deterministic: https://borsh.io ## Additional Resources - Whitepaper - General overview at [The Beginner's Guide to the NEAR Blockchain](https://near.org/blog/the-beginners-guide-to-the-near-blockchain) - [NEAR Whitepaper](https://near.org/papers/the-official-near-white-paper/) - Github - https://www.github.com/near :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/chain-abstraction/fastauth-sdk.md --- id: fastauth-sdk title: FastAuth SDK sidebar_label: FastAuth (Email Login) description: "Learn about FastAuth SDK, a key management system that enables users to recover or sign up for NEAR accounts using their email address." --- FastAuth is a key management system that allows users to **recover or sign-up for** a NEAR account using their **email address**. :::warning The current version of FastAuth is deprecated; a revamped version of the tooling using MPC and Auth0 is in the works. Stay tuned for updates. ::: --- # Source: https://docs.near.org/ai/shade-agents/concepts/framework-overview.md --- id: framework-overview title: Framework Overview sidebar_label: Framework Overview description: "Learn about the core components of the Shade Agent Framework with a high-level overview of each of its parts." --- The Shade Agent Framework provides a suite of tools designed to simplify the development and deployment of Shade Agents. The framework abstracts away the complexities of the underlying infrastructure, allowing developers to focus on building their agent logic. In this section, you'll explore the tooling provided by the framework and examine the key components you need when building an agent. --- ## Templates, Languages, and Architecture ### Templates When starting to build with the Shade Agent Framework, it's recommended to start by forking the [Quickstart Shade Agent Template](https://github.com/NearDeFi/shade-agent-template). This template contains all the necessary files to build a Shade Agent and provides the fastest starting path. Additional templates can be found in our [Tutorials and Templates](../tutorials/tutorials-overview.md) section. ### Supported Languages **TypeScript/JavaScript (Recommended)** Agents are primarily written in TypeScript/JavaScript using `shade-agent-js`, which integrates seamlessly with [chainsig.js](../../../chain-abstraction/chain-signatures/implementation.md) for building multichain transactions and deriving multichain accounts. **Python** The framework also supports `shade-agent-py`, which allows you to develop agents in Python. Here is an [example](https://github.com/NearDeFi/shade-python-example/tree/main). However, note that tooling for building multichain transactions and deriving multichain accounts is not currently available in Python, so additional development work will be required for multichain use cases. **Other Languages** Agents can be written in any language, provided you can create a Docker image for the agent. To build a Shade Agent in other languages, you can use the API directly. Learn more about this approach on the [API page](../reference/api.md). ### Architecture Overview A Shade Agent is essentially a `backend service` that uses the Shade Agent API and runs inside a Trusted Execution Environment (TEE) instead of on a classic server. You can develop using any backend framework you prefer, expose API routes, run cron jobs, or index events and respond to them with actions. --- ## Shade Agent API The Shade Agent API abstracts away the complexity of TEE operations and agent contract interactions. For detailed information on how the API works and how to use it across different languages, please refer to the [API page](../reference/api.md). --- ## Shade Agent CLI The Shade Agent CLI simplifies deploying a Shade Agent. To learn more about how the CLI works and how to use it, please refer to the [CLI page](../reference/cli.md). --- ## Environment Variables Environment variables are a crucial component of the Shade Agent Framework. They configure your Shade Agent and are passed encrypted into your agent when it goes live. To learn more about configuring environment variables in your project, please refer to the [Environment Variables page](../reference/environment-variables.md). --- ## Agent Contract By default, the Shade Agent CLI will deploy a generic agent contract that implements the three core functions, `approve_codehash`, `register_agent`, and `request_signature`, discussed in the introduction. This generic agent contract works for many use cases since you can register any arbitrary agent and have it request signatures for any chain - it's very flexible. There are also cases when you should develop your own `custom agent contract`. These include, but are not limited to: 1) You want to implement strict `guard rails` that prevent malicious actions, even if the TEE is somehow compromised - Review our [security considerations](../concepts/security.md#restricting-actions) for more details 2) You want to implement a custom agent registration or code hash upgradability mechanism 3) You want to build an agent that just interacts with the NEAR blockchain Further documentation can be found in the [custom contract section](../reference/custom-agent-contract.md). --- ## Phala Cloud Phala Cloud is a cloud solution that simplifies hosting applications and agents inside Trusted Execution Environments. The Shade Agent Framework uses Phala Cloud for agent deployment. You can deploy any standard Docker application to Phala. To learn more about Phala, visit their [documentation](https://docs.phala.network/phala-cloud/what-is/what-is-phala-cloud). Once your agent is deployed, you can manage the deployment from the [dashboard](https://cloud.phala.network/dashboard). To deploy an agent to production, you'll need a Phala Cloud account. You can create one [here](https://cloud.phala.network/register). After deploying to Phala Cloud, monitor your deployments and delete unused ones to avoid unnecessary costs. --- ## Docker Docker is a platform that allows you to package an application into a self-contained environment. By creating a Docker image, you can run your agent in the TEE. An agent typically consists of two Docker images (the application and the Shade Agent API), but it can include more. When building Shade Agents, it's helpful to understand how Docker works. If you're interested in learning more about Docker, please visit the [documentation](https://docs.docker.com/get-started/docker-overview/). You'll need to set up Docker on your machine if you do not have it already, and create an account: - Install Docker for [Mac](https://docs.docker.com/desktop/setup/install/mac-install/) or [Linux](https://docs.docker.com/desktop/setup/install/linux/) and create an account. - Log in to Docker, using `docker login` for Mac or `sudo docker login` for Linux. There are two Docker-related files included in our project: the `Dockerfile` and the `Docker Compose` file. ### Dockerfile The Dockerfile tells Docker how to build and run your image. The Shade Agent CLI automatically builds your Docker image using the Dockerfile and pushes it to Docker Hub, making it accessible over the internet. A standard Dockerfile will: 1. Start with a `base image`, which serves as the starting point for your application (e.g., Ubuntu, Alpine, Node.js, Python pre-installed) 2. Set the working directory 3. Install system dependencies 4. Add relevant files from the project to the image (in our examples, everything within the `source folder` is included, along with the `manifest file` that lists your dependencies like the package.json or pyproject.toml) 5. Install project dependencies 6. Build the project 7. Set the environment (production, development) 8. Tell Docker how to start the application In most cases, you can use the Dockerfile already supplied in the template without modification. Here are example Dockerfiles for a [Typescript](https://github.com/NearDeFi/shade-agent-template/blob/main/Dockerfile) and [Python](https://github.com/NearDeFi/shade-python-example/blob/main/Dockerfile) agent. You can learn more about the Dockerfile [here](https://docs.docker.com/reference/dockerfile/) ### Docker Compose The Docker Compose file (docker-compose.yaml) defines which Docker images will be included within your agent. This file is what is actually uploaded to Phala Cloud to run your agent, which pulls the specified images on boot. The compose file also specifies which environment variables are passed to the images, whether images are exposed on ports, and other configuration details. The images used are automatically configured when you run the Shade Agent CLI. In most cases, you can use the Docker Compose file already supplied [in the template](https://github.com/NearDeFi/shade-agent-template/blob/main/docker-compose.yaml). However, if you want to include additional Docker images in your agent or use additional environment variables for your application, you'll need to edit the Docker Compose file. You can learn more about the Docker Compose file [here](https://docs.docker.com/reference/compose-file/) --- ## Next Steps Now that you have an overview of the framework, here are some great sections to explore next: 1. Framework components: [API](../reference/api.md), [CLI](../reference/cli.md), and [Environment Variables](../reference/environment-variables.md) 2. [Custom Contracts](../reference/custom-agent-contract.md) - build specialized agent contracts 3. [Plugins](../reference/plugins.md) - extend your agent's capabilities 4. [Tutorials and Templates](../tutorials/tutorials-overview.md) - get up and running with different Shade Agent architectures, and use cases as quickly as possible and learn how to build apps in full 4. [Security Considerations](../concepts/security.md) - check your agent abides by best practices --- # Source: https://docs.near.org/tutorials/examples/frontend-multiple-contracts.md --- id: frontend-multiple-contracts title: Frontend Interacting with Multiple Contracts sidebar_label: Use Multiple Contracts description: "Interact with multiple contracts in your frontend." --- This example showcases how to interact with multiple contracts from a single frontend. Particularly, this example shows how to: 1. Query data from multiple contracts. 2. Call methods in multiple contracts simultaneously. --- ## Query Data from Multiple Contracts To query multiple contracts simply perform multiple `view` calls: ``` const totalMessages = await wallet.viewMethod({method: 'total_messages', contractId: GUEST_ADDRESS }) const from_index = (totalMessages > 4? totalMessages - 4: 0).toString(); const latestMessages = await wallet.viewMethod({ method: 'get_messages', contractId: GUEST_ADDRESS, args: {from_index, limit: "4"} }); // handle UI stuff update_UI(currentGreeting, from_index, latestMessages); } ``` --- ## Dispatching Multiple Transactions The `wallet` object enables to dispatch multiple transactions simultaneously. However, please notice that the transactions execute independently. Dispatching multiple transactions at once is just a nice way to improve UX, because the user interacts with the wallet only once. ``` const guestTx = { receiverId: GUEST_ADDRESS, actions: [ // actions execute sequentially { type: 'FunctionCall', params: { methodName: 'add_message', args: { text: greeting.value }, gas: THIRTY_TGAS, deposit: GUEST_DEPOSIT } }, ] } const helloTx = { receiverId: HELLO_ADDRESS, actions: [ // if any action fails, they all rollback together { type: 'FunctionCall', params: { methodName: 'set_greeting', args: { greeting: greeting.value }, gas: THIRTY_TGAS, deposit: NO_DEPOSIT } } ] } // Sign **independent** transactions: If one fails, the other **DOES NOT** revert await wallet.signAndSendTransactions({ transactions: [ helloTx, guestTx ] }) } ``` In this example, the user signs two independent transactions: 1. A transaction to call `set_greeting` in our [Hello NEAR example](https://github.com/near-examples/hello-near-examples) 2. A transaction to call `add_message` in our [GuestBook example](https://github.com/near-examples/guest-book-examples) :::caution Even when the user accepts signing the transactions at the same time, the transactions remain **independent**. This is, if one fails, the other is **NOT** rolled back. ::: --- ## Batch Actions You can aggregate multiple [actions](../../smart-contracts/anatomy/actions.md) directed towards a same contract into a single transaction. Batched actions execute **sequentially**, with the added benefit that, if **one fails** then they **all** get reverted. ```js // Register a user and transfer them FT on a single take const REGISTER_DEPOSIT = "1250000000000000000000"; const ftTx = { receiverId: FT_ADDRESS, actions: [ { type: 'FunctionCall', params: { methodName: 'storage_deposit', args: { account_id: "" }, gas: THIRTY_TGAS, deposit: REGISTER_DEPOSIT } }, { type: 'FunctionCall', params: { methodName: 'ft_transfer', args: { receiver_id: "", amount: amount_in_yocto }, gas: THIRTY_TGAS, deposit: 1 } } ] } // Ask the wallet to sign and send the transaction await wallet.signAndSendTransactions({ transactions: [ ftTx ] }) ``` --- # Source: https://docs.near.org/smart-contracts/security/frontrunning.md --- id: frontrunning title: Front Running description: "Learn about frontrunning attacks in NEAR smart contracts and how to prevent them with proper transaction ordering and MEV protection techniques." --- In the NEAR network, validators have access to the transaction pool, and can therefore see them before they execute. This enables validators to analyze transactions for a potential profit and frontrun them with a transaction of their own. For example, imagine that you make a game where users are paid for solving puzzles. If not handled carefully, a validator could swap a transaction with the valid answer for one of its own and claim the prize. You can read more about this in [this blog post](https://www.paradigm.xyz/2020/08/ethereum-is-a-dark-forest). --- # Source: https://docs.near.org/primitives/ft/ft.md --- id: ft title: Using FTs description: "Learn how to create, transfer, and integrate FT in your dApp" --- Wanting to use Fungible Tokens (FT) in your dApp? Here you will find all the information you need to get started creating your own tokens, registering users , querying balances, transferring tokens, and integrating them into your smart contracts. --- ## Creating a New Token The simplest way to create a new Fungible Token is by interacting with a factory contract, to which you only need to provide the token metadata, and they will automatically deploy and initialize a [canonical FT contract](https://github.com/near-examples/FT). Here you can make a call to the `token.primitives.testnet` by filling out the form below:
Manual Interaction Here is how to directly interact with the factory contract through your application: ```js import { useNearWallet } from "near-connect-hooks"; const TOKEN_FACTORY_ADDRESS = 'token.primitives.near'; const { callFunction } = useNearWallet(); const args = { args: { owner_id: 'bob.near', total_supply: '1000000000', metadata: { spec: 'ft-1.0.0', name: 'Test Token', symbol: 'test', icon: 'data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7', decimals: 18, }, }, account_id: 'bob.near', }; await callFunction({ contractId: TOKEN_FACTORY_ADDRESS, method: 'create_token', args, gas: 300000000000000, deposit: '2234830000000000000000', }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call token.primitives.near create_token '{"args":{"owner_id": "bob.near","total_supply": "1000000000","metadata":{"spec": "ft-1.0.0","name": "Test Token","symbol": "TTTEST","icon": "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7","decimals": 18}},"account_id": "bob.near"}' --gas 300000000000000 --depositYocto 2234830000000000000000000 --useAccount bob.near ``` }> The FT you create will live in the account `.token.primitives.near` (e.g. `test.token.primitives.near`).
--- ## Deploying Your Own Contract You can also create a fungible token by deploying and initializing a [canonical FT contract](https://github.com/near-examples/FT). On initialization, you will define the token's metadata such as its name (e.g. Ethereum), symbol (e.g. ETH) and total supply (e.g. 10M). You will also define an `owner`, which will own the tokens **total supply**. To initialize a FT contract you will need to deploy it and then call the `new` method defining the token's metadata. ```bash cargo near deploy build-non-reproducible-wasm \ with-init-call new \ json-args '{ "owner_id": "", "total_supply": "1000000000000000", "metadata": { "spec": "ft-1.0.0", "name": "Example Token Name", "symbol": "EXLT", "decimals": 8 } }' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain send ``` }>
### Global Contracts You can deploy a new Fungible Token using our global FT contract - a pre-deployed [standard FT contract](https://github.com/near-examples/FT) that you can reuse. [Global contracts](../../smart-contracts/global-contracts.md) are deployed once and can be reused by any account without incurring high storage costs. ```bash near contract deploy use-global-account-id ft.globals.primitives.testnet \ with-init-call \ new_default_meta \ json-args '{"owner_id": "", "total_supply": "100000000000000000000000000000"}' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain send ``` ```bash near contract deploy use-global-hash 3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX \ with-init-call \ new_default_meta \ json-args '{"owner_id": "", "total_supply": "100000000000000000000000000000"}' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain send ``` :::note Deploying by **hash** creates an immutable contract that never changes. Deploying by **account ID** creates an updatable contract that changes when the referenced account's contract is updated. Choose based on whether you want your FT contract to be updatable or permanent. ::: --- ## Querying Metadata You can query the FT's metadata by calling the `ft_metadata`. ```js import { useNearWallet } from "near-connect-hooks"; const TOKEN_CONTRACT_ADDRESS = 'token.v2.ref-finance.near'; const { viewFunction } = useNearWallet(); await viewFunction({ method: 'ft_metadata', args: {}, contractId: TOKEN_CONTRACT_ADDRESS, }); ```
Example response

```json { "spec": "ft-1.0.0", "name": "Ref Finance Token", "symbol": "REF", "icon": "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='16 24 248 248' style='background: %23000'%3E%3Cpath d='M164,164v52h52Zm-45-45,20.4,20.4,20.6-20.6V81H119Zm0,18.39V216h41V137.19l-20.6,20.6ZM166.5,81H164v33.81l26.16-26.17A40.29,40.29,0,0,0,166.5,81ZM72,153.19V216h43V133.4l-11.6-11.61Zm0-18.38,31.4-31.4L115,115V81H72ZM207,121.5h0a40.29,40.29,0,0,0-7.64-23.66L164,133.19V162h2.5A40.5,40.5,0,0,0,207,121.5Z' fill='%23fff'/%3E%3Cpath d='M189 72l27 27V72h-27z' fill='%2300c08b'/%3E%3C/svg%3E%0A", "reference": null, "reference_hash": null, "decimals": 18 } ```

Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application
```bash near view token.v2.ref-finance.near ft_metadata ```
Example response

```bash { spec: "ft-1.0.0", name: "Ref Finance Token", symbol: "REF", icon: "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='16 24 248 248' style='background: %23000'%3E%3Cpath d='M164,164v52h52Zm-45-45,20.4,20.4,20.6-20.6V81H119Zm0,18.39V216h41V137.19l-20.6,20.6ZM166.5,81H164v33.81l26.16-26.17A40.29,40.29,0,0,0,166.5,81ZM72,153.19V216h43V133.4l-11.6-11.61Zm0-18.38,31.4-31.4L115,115V81H72ZM207,121.5h0a40.29,40.29,0,0,0-7.64-23.66L164,133.19V162h2.5A40.5,40.5,0,0,0,207,121.5Z' fill='%23fff'/%3E%3Cpath d='M189 72l27 27V72h-27z' fill='%2300c08b'/%3E%3C/svg%3E%0A", reference: null, reference_hash: null, decimals: 18 } ```

}>
--- ## Checking Balance To know how many coins a user has you will need to query the method `ft_balance_of`. :::info Remember about fungible token precision. You may need this value to show a response of balance requests in an understandable-to-user way in your app. How to get precision value (decimals) you may find [here](#querying-metadata). ::: ```js import { useNearWallet } from "near-connect-hooks"; const TOKEN_CONTRACT_ADDRESS = 'token.v2.ref-finance.near'; const { viewFunction } = useNearWallet(); await viewFunction({ method: 'ft_balance_of', args: { account_id: 'bob.near', }, contractId: TOKEN_CONTRACT_ADDRESS, }); ```
Example response

```json "3479615037675962643842" ```

Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application
```bash near view token.v2.ref-finance.near ft_balance_of '{"account_id": "bob.near"}' ```
Example response

```bash '376224322825327177426' ```

}>
--- ## Registering a User In order for a user to own and transfer tokens they need to first **register** in the contract. This is done by calling `storage_deposit` and attaching 0.00125Ⓝ. By calling this `storage_deposit` the user can register themselves or **register other users**. ```js import { useNearWallet } from "near-connect-hooks"; const TOKEN_CONTRACT_ADDRESS = 'token.v2.ref-finance.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: TOKEN_CONTRACT_ADDRESS, method: 'storage_deposit', args: { account_id: 'alice.near', }, deposit: 1250000000000000000000, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call token.v2.ref-finance.near storage_deposit '{"account_id": "alice.near"}' --depositYocto 1250000000000000000000 --useAccount bob.near ``` }> :::info You can make sure a user is registered by calling `storage_balance_of`. ::: :::tip After a user calls the `storage_deposit` the FT will appear in their Wallets. ::: --- ## Transferring Tokens To send FT to another account you will use the `ft_transfer` method, indicating the receiver and the amount of FT you want to send. ```js import { useNearWallet } from "near-connect-hooks"; const TOKEN_CONTRACT_ADDRESS = 'token.v2.ref-finance.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: TOKEN_CONTRACT_ADDRESS, method: 'ft_transfer', args: { receiver_id: 'alice.near', amount: '100000000000000000', }, deposit: 1, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call token.v2.ref-finance.near ft_transfer '{"receiver_id": "alice.near", "amount": "100000000000000000"}' --depositYocto 1 --useAccount bob.near ``` }> ```rust #[near] impl Contract { #[payable] pub fn send_tokens(&mut self, receiver_id: AccountId, amount: U128) -> Promise { assert_eq!(env::attached_deposit(), 1, "Requires attached deposit of exactly 1 yoctoNEAR"); let promise = ext(self.ft_contract.clone()) .with_attached_deposit(YOCTO_NEAR) .ft_transfer(receiver_id, amount, None); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(Gas(30*TGAS)) .external_call_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn external_call_callback(&self, #[callback_result] call_result: Result<(), PromiseError>) { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting external contract"); } } } ``` _This snippet assumes that the contract is already holding some FTs and that you want to send them to another account._ --- ## Attaching FTs to a Call Natively, only NEAR tokens (Ⓝ) can be attached to a function calls. However, the FT standard enables to **attach fungible tokens** in a call by using the FT-contract as intermediary. This means that, instead of you attaching tokens directly to the call, you ask the FT-contract to do both a transfer and a function call in your name. Let's assume that you need to deposit FTs on [Ref Finance](https://rhea.finance/). ```js import { useNearWallet } from "near-connect-hooks"; const TOKEN_CONTRACT_ADDRESS = 'token.v2.ref-finance.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: TOKEN_CONTRACT_ADDRESS, method: 'ft_transfer_call', args: { receiver_id: 'v2.ref-finance.near', amount: '100000000000000000', msg: '', }, gas: 300000000000000, deposit: 1, }); ```
Example response

```json "100000000000000000" ```

Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application
```bash near call token.v2.ref-finance.near ft_transfer_call '{"receiver_id": "v2.ref-finance.near", "amount": "100000000000000000", "msg": ""}' --gas 300000000000000 --depositYocto 1 --useAccount bob.near ```
Example response

```bash '100000000000000000' ```

}> ```rust #[payable] pub fn call_with_attached_tokens(&mut self, receiver_id: AccountId, amount: U128) -> Promise { assert_eq!(env::attached_deposit(), 1, "Requires attached deposit of exactly 1 yoctoNEAR"); let promise = ext(self.ft_contract.clone()) .with_static_gas(Gas(150*TGAS)) .with_attached_deposit(YOCTO_NEAR) .ft_transfer_call(receiver_id, amount, None, "".to_string()); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(Gas(100*TGAS)) .external_call_callback() ) } ```
How it works: 1. You call `ft_transfer_call` in the FT contract passing: the receiver, a message, and the amount. 2. The FT contract transfers the amount to the receiver. 3. The FT contract calls `receiver.ft_on_transfer(sender, msg, amount)` 4. The FT contract handles errors in the `ft_resolve_transfer` callback. 5. The FT contract returns you how much of the attached amount was actually used. --- ## Handling Deposits If you want your contract to handle deposit in FTs you have to implement the `ft_on_transfer` method. When executed, such method will know: - Which FT was transferred, since it is the predecessor account. - Who is sending the FT, since it is a parameter - How many FT were transferred, since it is a parameter - If there are any parameters encoded as a message The `ft_on_transfer` must return how many FT tokens have to **be refunded**, so the FT contract gives them back to the sender. Here is an example from our [auctions tutorial](../../tutorials/auction/3.2-ft.md) where we implement `ft_on_transfer` to handle bids in FTs: ``` #[allow(unused_variables, unused_must_use)] pub fn ft_on_transfer(&mut self, sender_id: AccountId, amount: U128, msg: String) -> U128 { require!( env::block_timestamp() < self.auction_end_time.into(), "Auction has ended" ); let ft = env::predecessor_account_id(); require!(ft == self.ft_contract, "The token is not supported"); // Last bid let Bid { bidder: last_bidder, bid: last_bid, } = self.highest_bid.clone(); // Check if the deposit is higher than the current bid require!(amount > last_bid, "You must place a higher bid"); // Update the highest bid self.highest_bid = Bid { bidder: sender_id, bid: amount, }; // Transfer FTs back to the last bidder ft_contract::ext(self.ft_contract.clone()) .with_attached_deposit(NearToken::from_yoctonear(1)) .with_static_gas(Gas::from_tgas(30)) .ft_transfer(last_bidder, last_bid); U128(0) } ``` _Note: The [`near_contract_standards::fungible_token::receiver`](https://docs.rs/near-contract-standards/latest/near_contract_standards/fungible_token/receiver/trait.FungibleTokenReceiver.html) module exposes a `FungibleTokenReceiver` trait that you could implement on your contract_ --- ## Burn Tokens While the FT standard does not define a `burn` method, you can simply transfer tokens to an account that no one controls, such as [`0000000000000000000000000000000000000000000000000000000000000000`](https://nearblocks.io/es/address/0000000000000000000000000000000000000000000000000000000000000000) (64 zeros). ```js import { useNearWallet } from "near-connect-hooks"; const { callFunction } = useNearWallet(); await callFunction({ contractId: 'token.v2.ref-finance.near', method: 'ft_transfer', args: { receiver_id: '0000000000000000000000000000000000000000000000000000000000000000', amount: '100000000000000000', }, deposit: 1, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call token.v2.ref-finance.near ft_transfer '{"receiver_id": "0000000000000000000000000000000000000000000000000000000000000000", "amount": "100000000000000000"}' --depositYocto 1 --useAccount bob.near ``` --- ## Additional Resources 1. [NEP-141 standard](https://github.com/near/NEPs/tree/master/neps/nep-0141.md) 2. [NEP-148 standard](https://github.com/near/NEPs/tree/master/neps/nep-0148.md) 3. [FT Event Standards](https://github.com/near/NEPs/blob/master/neps/nep-0300.md) 4. [FT reference implementation](https://github.com/near-examples/FT) 5. [Fungible Tokens 101](../../tutorials/fts.md) - a set of tutorials that cover how to create a FT contract using Rust. --- # Source: https://docs.near.org/tutorials/fts.md --- id: fts title: Fungible Tokens Zero to Hero sidebar_label: Build a FT Contract from Scratch description: "Master NEAR fungible tokens from pre-deployed contracts to building fully-featured FT smart contracts." --- In this _Zero to Hero_ series, you'll find a set of tutorials covering every aspect of a fungible token (FT) smart contract. You'll start by interacting with a pre-deployed contract and by the end you'll have built a fully-fledged FT smart contract that supports every extension of the standards. --- ## Prerequisites To complete these tutorials successfully, you'll need: - [Rust](/smart-contracts/quickstart#prerequisites) - [A NEAR wallet](https://testnet.mynearwallet.com) - [NEAR-CLI](/tools/near-cli#installation) - [cargo-near](https://github.com/near/cargo-near) :::info New to Rust? If you are new to Rust and want to dive into smart contract development, our [Quick-start guide](https://docs.near.org/smart-contracts/quickstart) is a great place to start. ::: --- ## Overview These are the steps that will bring you from **_Zero_** to **_Hero_** in no time! 💪 | Step | Name | Description | | ---- | ------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | 1 | [Pre-deployed contract](https://near-examples.github.io/ft-tutorial/predeployed-contract) | Receive FTs without the need to code, create, or deploy a smart contract. | | 2 | [Contract architecture](https://near-examples.github.io/ft-tutorial/skeleton) | Learn the basic architecture of the FT smart contract and compile the code. | | 3 | [Defining a Token](https://near-examples.github.io/ft-tutorial/defining-a-token) | Flesh out what it means to have a FT and how you can customize your own. | | 4 | [Circulating Supply](https://near-examples.github.io/ft-tutorial/circulating-supply) | Learn how to create an initial supply and have the token show up in your wallet. | | 5 | [Registering Accounts](https://near-examples.github.io/ft-tutorial/registering-accounts) | Explore how you can implement and understand the storage management standard to avoid malicious users from draining your funds. | | 6 | [Transferring FTs](https://near-examples.github.io/ft-tutorial/transfers) | Learn how to transfer FTs and discover some of the true powers that the core standard brings | | 7 | [Marketplace](https://near-examples.github.io/ft-tutorial/marketplace) | Learn about how common marketplaces operate on NEAR and dive into some of the code that allows buying and selling NFTs by using Fungible Tokens. | --- ## Next steps Ready to start? Jump to the [Pre-deployed Contract](https://near-examples.github.io/ft-tutorial/predeployed-contract) tutorial and begin your learning journey! If you already know about fungible tokens and smart contracts, feel free to skip and jump directly to the tutorial of your interest. The tutorials have been designed so you can start at any given point! --- # Source: https://docs.near.org/smart-contracts/anatomy/functions.md --- id: functions title: External Interface hide_table_of_contents: true description: "Learn how to define your contract's interface." --- Smart contracts expose functions so users can interact with them. There are different types of functions including `read-only`, `private` and `payable`. ### Contract's Interface All **public** functions in the contract are part of its **interface**. They can be called by anyone, and are the only way to interact with the contract.
Exposing trait implementations Functions can also be exposed through trait implementations. This can be useful if implementing a shared interface or standard for a contract. This code generation is handled very similarly to basic `pub` functions, but the `#[near]` macro only needs to be attached to the trait implementation, not the trait itself: ```rust pub trait MyTrait { fn trait_method(&mut self); } #[near] impl MyTrait for MyContractStructure { fn trait_method(&mut self) { // .. method logic here } } ```
### Initialization Functions A contract can opt to have an initialization function. If present, this function must be called before any other to [initialize the contract](./storage.md). #### `@initialize({ privateFunction: true })` The initialization function is marked with the `@initialize` decorator. #### `#[init]` The initialization function is marked with the `#[init]` macro. #### `@init` The initialization function is marked with the `@init` decorator in Python. ### State Changing Functions The functions that modify the [state](./storage.md) or perform [actions](./actions.md) need to be called by a user with a NEAR account, since a transaction is required to execute them. #### `@call` State changing functions are marked with the `@call` decorator. #### `&mut self` State changing functions are those that take a **mutable** reference to `self` in Rust. #### `@call` State changing functions are marked with the `@call` decorator in Python. :::tip The SDK provides [contextual information](./environment.md), such as which account is calling the function, or what time it is. ::: ### Read-Only Functions Contract's functions can be read-only, meaning they don't modify the state. Calling them is free for everyone, and does not require to have a NEAR account. #### `@view` Read-only functions are marked with the `@view` decorator in TS/JS. #### `&self` Read-only functions are those that take an **immutable** reference to `self` in Rust. #### `@view` Read-only functions are marked with the `@view` decorator in Python. ### Private Functions Many times you will want to have functions that **are exposed** as part of the contract's interface, but **should not be called directly** by users. Besides initialization functions, [callbacks from cross-contract calls](./crosscontract.md) should always be `private`. These functions are marked as `private` in the contract's code, and can only be called by the contract itself. #### `decorator({privateFunction: true})` Private functions are marked by setting `privateFunction: true` in the `@call` or `@initialize` decorators. #### [#private] Private functions are marked using the `#[private]` macro in Rust. #### Private Functions in Python In Python, you can create callbacks by using the `@callback` decorator, which is designed specifically for handling cross-contract call results. For general private functions that should only be called by the contract, you can use validation inside the function to check that the caller is the contract itself. ### Payable Functions By default, functions will panic if the user attaches NEAR Tokens to the call. Functions that accept NEAR Tokens must be marked as `payable`. Within the function, the user will have access to the [attached deposit](./environment.md). #### `@call({payableFunction: true})` Payable functions are marked by setting `payableFunction: true` in the `@call` decorator. #### [#payable] Payable functions are marked using the `#[payable]` macro in Rust. #### Handling payments in Python In Python, you need to check the deposit manually using the Context API. There isn't a specific decorator for payable functions, so you'll need to verify the deposit amount in your function code. ### Internal Functions All the functions we covered so far are part of the interface, meaning they can be called by an external actor. However, contracts can also have private internal functions - such as helper or utility functions - that are **not exposed** to the outside world. To create internal private methods in a JS contract, simply omit the `@view` and `@call` decorators. ### Internal Functions All the functions we covered so far are part of the interface, meaning they can be called by an external actor. However, contracts can also have private internal functions - such as helper or utility functions - that are **not exposed** to the outside world. To create internal private methods in a Rust contract, do not declare them as public (`pub fn`). ### Internal Functions All the functions we covered so far are part of the interface, meaning they can be called by an external actor. However, contracts can also have private internal functions - such as helper or utility functions - that are **not exposed** to the outside world. To create internal private methods in a Python contract, simply define normal methods without the `@view`, `@call`, or `@init` decorators.
Separate impl block Another way of not exporting methods is by having a separate `impl Contract` section, that is not marked with `#[near]`. ```rust #[near] impl Contract { pub fn increment(&mut self) { self.internal_increment(); } } impl Contract { /// This methods is still not exported. pub fn internal_increment(&mut self) { self.counter += 1; } } ```
### Pure Functions Pure functions are a special kind of function that do not require to access data from the state. They are useful to return hardcoded values on the contract. ```js import { NearBindgen, near, call, view, AccountId, NearPromise, initialize, assert } from "near-sdk-js"; class Bid { bidder: AccountId; bid: bigint; } @NearBindgen({ requireInit: true }) class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(0) }; auction_end_time: bigint = BigInt(0); auctioneer: AccountId = ""; claimed: boolean = false; @initialize({ privateFunction: true }) init({ end_time, auctioneer}: { end_time: bigint, auctioneer: AccountId}) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; this.auctioneer = auctioneer; } @call({ payableFunction: true }) bid(): NearPromise { // Assert the auction is still ongoing assert(this.auction_end_time > near.blockTimestamp(), "Auction has ended"); // Current bid const bid = near.attachedDeposit(); const bidder = near.predecessorAccountId(); // Last bid const { bidder: lastBidder, bid: lastBid } = this.highest_bid; // Check if the deposit is higher than the current bid assert(bid > lastBid, "You must place a higher bid"); // Update the highest bid this.highest_bid = { bidder, bid }; // Save the new bid // Transfer tokens back to the last bidder return NearPromise.new(lastBidder).transfer(lastBid); } @call({}) claim() { assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); assert(!this.claimed, "Auction has been claimed"); this.claimed = true; return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) } @view({}) get_highest_bid(): Bid { return this.highest_bid; } @view({}) get_auction_end_time(): BigInt { return this.auction_end_time; } ``` ```rust use near_sdk::json_types::U64; use near_sdk::{env, near, require, AccountId, NearToken, PanicOnDefault, Promise}; #[near(serializers = [json, borsh])] #[derive(Clone)] pub struct Bid { pub bidder: AccountId, pub bid: NearToken, } #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { highest_bid: Bid, auction_end_time: U64, auctioneer: AccountId, claimed: bool, } #[near] impl Contract { #[init] #[private] // only callable by the contract's account pub fn init(end_time: U64, auctioneer: AccountId) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: NearToken::from_yoctonear(1), }, auction_end_time: end_time, claimed: false, auctioneer, } } #[payable] pub fn bid(&mut self) -> Promise { // Assert the auction is still ongoing require!( env::block_timestamp() < self.auction_end_time.into(), "Auction has ended" ); // Current bid let bid = env::attached_deposit(); let bidder = env::predecessor_account_id(); // Last bid let Bid { bidder: last_bidder, bid: last_bid, } = self.highest_bid.clone(); // Check if the deposit is higher than the current bid require!(bid > last_bid, "You must place a higher bid"); // Update the highest bid self.highest_bid = Bid { bidder, bid }; // Transfer tokens back to the last bidder Promise::new(last_bidder).transfer(last_bid) } pub fn claim(&mut self) -> Promise { require!( env::block_timestamp() > self.auction_end_time.into(), "Auction has not ended yet" ); require!(!self.claimed, "Auction has already been claimed"); self.claimed = true; // Transfer tokens to the auctioneer Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid) } pub fn get_highest_bid(&self) -> Bid { self.highest_bid.clone() } pub fn get_auction_end_time(&self) -> U64 { self.auction_end_time } ``` ```python class AuctionContract(Contract): @init def initialize(self, end_time, auctioneer): """Initialize the auction contract with an end time and auctioneer account""" # Save auction end time self.storage["auction_end_time"] = end_time # Initialize highest bid with current account and 1 yocto initial_bid = { "bidder": self.current_account_id, "bid": ONE_YOCTO } self.storage["highest_bid"] = initial_bid # Set auctioneer self.storage["auctioneer"] = auctioneer # Initialize claimed status self.storage["claimed"] = False return {"success": True} @call def bid(self): """Place a bid in the auction""" # Get auction end time auction_end_time = self.storage["auction_end_time"] # Assert the auction is still ongoing current_time = self.block_timestamp if current_time >= auction_end_time: raise InvalidInput("Auction has ended") # Current bid bid_amount = self.attached_deposit bidder = self.predecessor_account_id # Get last bid highest_bid = self.storage["highest_bid"] last_bidder = highest_bid["bidder"] last_bid = highest_bid["bid"] # Check if the deposit is higher than the current bid if bid_amount <= last_bid: raise InvalidInput("You must place a higher bid") # Update the highest bid new_highest_bid = { "bidder": bidder, "bid": bid_amount } self.storage["highest_bid"] = new_highest_bid # Log the new bid self.log_event("new_bid", { "bidder": bidder, "amount": bid_amount }) # Transfer tokens back to the last bidder return CrossContract(last_bidder).transfer(last_bid).value() @call def claim(self): """Claim the auction proceeds after it has ended""" # Get auction end time and claimed status auction_end_time = self.storage["auction_end_time"] claimed = self.storage.get("claimed", False) # Assert the auction has ended current_time = self.block_timestamp if current_time <= auction_end_time: raise InvalidInput("Auction has not ended yet") # Assert the auction has not been claimed if claimed: raise InvalidInput("Auction has already been claimed") # Mark as claimed self.storage["claimed"] = True # Get highest bid and auctioneer highest_bid = self.storage["highest_bid"] auctioneer = self.storage["auctioneer"] # Log the claim self.log_event("auction_claimed", { "winner": highest_bid["bidder"], "amount": highest_bid["bid"] }) # Transfer tokens to the auctioneer return CrossContract(auctioneer).transfer(highest_bid["bid"]).value() @view def get_highest_bid(self): """Returns the current highest bid information""" return self.storage.get("highest_bid") @view def get_auction_end_time(self): """Returns the auction end time""" return self.storage.get("auction_end_time") @view def get_auction_status(self): """Returns the overall status of the auction""" current_time = self.block_timestamp auction_end_time = self.storage["auction_end_time"] highest_bid = self.storage["highest_bid"] claimed = self.storage.get("claimed", False) return { "is_active": current_time < auction_end_time, "time_remaining": max(0, auction_end_time - current_time), "highest_bidder": highest_bid["bidder"], "highest_bid": highest_bid["bid"], "claimed": claimed, "auctioneer": self.storage["auctioneer"] } ``` ```js @NearBindgen({}) class Contract { helper_function(params... ){ // this function cannot be called from the outside } @view({}) interface_view(params...){ // this function can be called from outside } @call({privateFunction: true}){ // this function can be called from outside, but // only by the contract's account } } ``` ```rs const SOME_VALUE: u64 = 8; #[near] impl MyContractStructure { fn internal_helper(&mut self, params... ){ // this function cannot be called from the outside } pub fn public_log(/* Parameters here */) { near_sdk::log!("inside log message"); } pub fn return_static_u64() -> u64 { SOME_VALUE } } ``` ```python class Contract: def helper_function(self, params...): # This function cannot be called from the outside # as it has no decorator pass @call def validate_owner(self): if Context.predecessor_account_id() != self.owner_id: raise Exception("Only the owner can call this method") @view def get_static_value(self): # This function returns a hardcoded value return 42 ```
--- # Source: https://docs.near.org/integrations/fungible-tokens.md --- id: fungible-tokens title: Fungible tokens sidebar_label: Fungible Tokens description: "Learn how to integrate NEAR tokens (NEAR, FT, and NFT) into your application, including balance queries, transfers, and token metadata." --- This document provides an overview of the NEAR fungible token standard, including how to check balances, get metadata, and perform transfers. It also covers the storage requirements for fungible tokens and how to manage storage deposits. ## Introduction {#introduction} Please see the [spec for the fungible token standard](https://github.com/near/NEPs/tree/master/neps/nep-0141.md) and an [example implementation](https://github.com/near-examples/FT) for reference details. One notable aspect of the standard is that method names are prefixed with `ft_`. This will be a helpful convention when querying for transactions related to fungible tokens. ## Get balance {#get-balance} Using the abstraction of the [NEAR CLI](/tools/near-cli) tool, we can check the balance of a user's account with [`near view`](/tools/near-cli#call): `near view ft.demo.testnet ft_balance_of '{"account_id": "mike.testnet"}'` Returns: ``` View call: ft.demo.testnet.ft_balance_of({"account_id": "mike.testnet"}) '1000000' ``` Alternatively, you can [call a contract function](/api/rpc/contracts#call-a-contract-function) using the `query` RPC endpoint. Below is an example using HTTPie: ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=ftbalance method=query \ params:='{ "request_type": "call_function", "finality": "final", "account_id": "ft.demo.testnet", "method_name": "ft_balance_of", "args_base64": "eyJhY2NvdW50X2lkIjogIm1pa2UudGVzdG5ldCJ9" }' ``` Returns: ```bash HTTP/1.1 200 OK Alt-Svc: clear Via: 1.1 google access-control-allow-origin: content-length: 176 content-type: application/json date: Thu, 27 May 2021 12:53:38 GMT { "id": "dontcare", "jsonrpc": "2.0", "result": { "block_hash": "3mvNHpZAsXiJ6SuHU1mbLVB4iXCfh5i5d41pnkaSoaJ5", "block_height": 49282350, "logs": [], "result": [ 34, 49, 48, 48, 48, 48, 48, 48, 34 ] } } ``` As mentioned earlier, the `result` is an array of bytes. There are various ways to convert bytes into a more human-readable form such as the [dtool CLI](https://github.com/guoxbin/dtool#installation). `dtool a2h '[34,49,48,48,48,48,48,48,34]' | dtool h2s` Returns: `"1000000"` **Note:** The fungible token balance of the account `mike.testnet` is `1000000` wrapped in double-quotes. This is because of an issue with JSON serialization. Amounts given in arguments and results must be serialized as Base-10 strings, e.g. "100". This is done to avoid JSON limitation of max integer value of 2**53, which can certainly happen with fungible tokens. ## Get info about the FT {#get-info-about-the-ft} You can get `name`, `decimals`, `icon` and other parameters by calling the next function: - using NEAR CLI: ```bash near view ft_metadata ``` Result: ```bash View call: ft.demo.testnet.ft_metadata() { spec: 'ft-1.0.0', name: 'Example Token Name', symbol: 'MOCHI', icon: null, reference: null, reference_hash: null, decimals: 24 } ``` - with JSON RPC call: ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=ftmetadata method=query \ params:='{ "request_type": "call_function", "finality": "final", "account_id": "", "method_name": "ft_metadata", "args_base64": "" }' ``` Example response: ```bash HTTP/1.1 200 OK Alt-Svc: clear Via: 1.1 google access-control-allow-origin: content-length: 604 content-type: application/json date: Wed, 02 Jun 2021 15:51:17 GMT { "id": "ftmetadata", "jsonrpc": "2.0", "result": { "block_hash": "B3fu3v4dmn19B6oqjHUXN3k5NhdP9EW5kkjyuFUDpa1r", "block_height": 50061565, "logs": [], "result": [ 123, 34, 115, 112, 101, 99, 34, 58, 34, 102, 116, 45, 49, 46, 48, 46, 48, 34, 44, 34, 110, 97, 109, 101, 34, 58, 34, 69, 120, 97, 109, 112, 108, 101, 32, 84, 111, 107, 101, 110, 32, 78, 97, 109, 101, 34, 44, 34, 115, 121, 109, 98, 111, 108, 34, 58, 34, 77, 79, 67, 72, 73, 34, 44, 34, 105, 99, 111, 110, 34, 58, 110, 117, 108, 108, 44, 34, 114, 101, 102, 101, 114, 101, 110, 99, 101, 34, 58, 110, 117, 108, 108, 44, 34, 114, 101, 102, 101, 114, 101, 110, 99, 101, 95, 104, 97, 115, 104, 34, 58, 110, 117, 108, 108, 44, 34, 100, 101, 99, 105, 109, 97, 108, 115, 34, 58, 50, 52, 125 ] } } ``` Decoded result in this case is: ```json { "spec": "ft-1.0.0", "name": "Example Token Name", "symbol": "MOCHI", "icon": null, "reference": null, "reference_hash": null, "decimals": 24 } ``` ## Simple transfer {#simple-transfer} To follow this guide, please check the [step by step instructions](/integrations/create-transactions#low-level----create-a-transaction) on how to create a transaction first. In order to send a fungible token to an account, the receiver must have a storage deposit. This is because each smart contract on NEAR must account for storage used, and each account on a fungible token contract is a key-value pair, taking up a small amount of storage. For more information, please see [how storage works in NEAR](/protocol/storage/storage-staking). To check if account has deposited the storage for this FT do the following: Get storage balance of the account. `storage_balance_of` function returns the amount of deposited storage or `null` if there is no deposit. - using NEAR CLI: ```bash near view storage_balance_of '{"account_id": ""}' ``` Result: ```bash View call: ft.demo.testnet.storage_balance_of({"account_id": "serhii.testnet"}) null ``` - with JSON RPC call: ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=storagebalanceof method=query \ params:='{ "request_type": "call_function", "finality": "final", "account_id": "ft.demo.testnet", "method_name": "storage_balance_of", "args_base64": "eyJhY2NvdW50X2lkIjogInNlcmhpaS50ZXN0bmV0In0K" }' ``` Example response: ```bash HTTP/1.1 200 OK Alt-Svc: clear Via: 1.1 google access-control-allow-origin: content-length: 173 content-type: application/json date: Wed, 02 Jun 2021 14:22:01 GMT { "id": "storagebalanceof", "jsonrpc": "2.0", "result": { "block_hash": "EkM2j4yxRVoQ1TCqF2KUb7J4w5G1VsWtMLiycq6k3f53", "block_height": 50054247, "logs": [], "result": [ 110, 117, 108, 108 ] } } ``` Decoded result in this case is `null`. Get the minimum storage required for FT. (The storage used for an account's key-value pair.) - using NEAR CLI: ```bash near view storage_balance_bounds` ``` Result: ```bash View call: ft.demo.testnet.storage_balance_bounds() { min: '1250000000000000000000', max: '1250000000000000000000' } ``` - with JSON RPC call ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=storagebalancebounds method=query \ params:='{ "request_type": "call_function", "finality": "final", "account_id": "", "method_name": "storage_balance_bounds", "args_base64": "" }' ``` Example response: ```bash HTTP/1.1 200 OK Alt-Svc: clear Via: 1.1 google access-control-allow-origin: content-length: 357 content-type: application/json date: Wed, 02 Jun 2021 15:42:49 GMT { "id": "storagebalancebounds", "jsonrpc": "2.0", "result": { "block_hash": "Fy3mBqwj5nvUDha3X7G61kmUeituHASEX12oCASrChEE", "block_height": 50060878, "logs": [], "result": [ 123, 34, 109, 105, 110, 34, 58, 34, 49, 50, 53, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 44, 34, 109, 97, 120, 34, 58, 34, 49, 50, 53, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34, 125 ] } } ``` Decoded result should look similar to: ```json { "min": "1250000000000000000000", "max": "1250000000000000000000" } ``` Basic fungible tokens are simple smart contracts that don't have variable storage as compared to a smart contract that might store free-form text, for instance. The only storage needed is for an accounts key-value pair, which will always be covered by the `1250000000000000000000` yoctoⓃ storage balance. If there is not enough deposit for the storage or returned value is `null` - you should deposit more storage with the next command: - using NEAR CLI, don't forget to convert from yoctoⓃ to Ⓝ: ```bash near contract call-function as-transaction storage_deposit json-args '{"account_id": ""}' prepaid-gas '100.0 Tgas' attached-deposit ' NEAR' sign-as network-config testnet sign-with-keychain send ``` Result example: ```bash Scheduling a call: ft.demo.testnet.storage_deposit() with attached 0.125 NEAR Transaction Id 9CMrMMt3UzeU63FFrUyFb1gNGuHXxvKfHqYJzyFTAk6z To see the transaction in the transaction explorer, please open this url in your browser https://testnet.nearblocks.io/txns/9CMrMMt3UzeU63FFrUyFb1gNGuHXxvKfHqYJzyFTAk6z { total: '1250000000000000000000', available: '0' } ``` - with JSON RPC call: At the top of this section is a link detailing how to [construct a transaction](/integrations/create-transactions#low-level----create-a-transaction) without the full abstraction of the [`near-api-js` library](https://www.npmjs.com/package/near-api-js). For this and future examples that use the [RPC method `broadcast_tx_commit`](/api/rpc/transactions#send-transaction-await) we will provide a JSON-like object meant to act similar to [pseudocode](https://en.wikipedia.org/wiki/Pseudocode), only imparting high-level details of a transaction. This code block below is the first example of this, detailing what goes into the transaction discussed currently, involving the method `storage_deposit`. ```yaml Transaction: { block_hash: `456…abc`, signer_id: "serhii.testnet", public_key: "ed25519:789…def", nonce: 123, receiver_id: "ft.demo.testnet", actions: [ FunctionCall( FunctionCallAction { method_name: storage_deposit, args: `{"account_id": "robertyan.near"}`, gas: 300000000000000, deposit: 1250000000000000000000, }, ), ] } ``` ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=broadcast_tx_commit \ params:='["DgAAAHNlcmhpaS50ZXN0bmV0AEKEp54fyVkp8dJE2l/m1ErjdhDGodBK8ZF6JLeHFMeZi/qoVEgrAAAPAAAAZnQuZGVtby50ZXN0bmV0JYbWPOu0P9T32vtUKnZSh+EaoboQqg0/De2i8Y+AjHIBAAAAAg8AAABzdG9yYWdlX2RlcG9zaXQCAAAAe30AQHoQ81oAAAAAILSd2XlDeBoAAAAAAAAAZF7+s4lcHOzy+re59VErt7LcZkPMMUVgOJV8LH5TsLBBv+8h/5tZ6+HFwxSp605A4c46oS9Jw4KBRXZD07lKCg=="]' ```
**Example Response:** ```json { "id": "myid", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "serhii.testnet", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "e30=", "deposit": "125000000000000000000000", "gas": 100000000000000, "method_name": "storage_deposit" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "4urgFabknn1myZkjTYdb1BFSoEimP21k9smCUWoSggA7", "receiver_id": "ft.demo.testnet" }, { "predecessor_id": "ft.demo.testnet", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "123750000000000000000000" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "7neJYE45vXnQia1LQqWuAfyTRXHy4vv88JaULa5DnNBd", "receiver_id": "serhii.testnet" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "19200274886926125000" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "2c59u2zYj41JuhMfPUCKjNucmYfz2Jt83JLWP6VyQn1S", "receiver_id": "serhii.testnet" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "18587201427159524319124" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "kaYatRKxcC1NXac69WwTqg6K13oXq2yEuy4LLZtsV2G", "receiver_id": "serhii.testnet" } ], "receipts_outcome": [ { "block_hash": "6Gz6P8N3F447kRc7kkxEhuZRZTzfuTUEagye65bPVGb", "id": "4urgFabknn1myZkjTYdb1BFSoEimP21k9smCUWoSggA7", "outcome": { "executor_id": "ft.demo.testnet", "gas_burnt": 4258977405434, "logs": [], "receipt_ids": [ "7neJYE45vXnQia1LQqWuAfyTRXHy4vv88JaULa5DnNBd", "kaYatRKxcC1NXac69WwTqg6K13oXq2yEuy4LLZtsV2G" ], "status": { "SuccessValue": "eyJ0b3RhbCI6IjEyNTAwMDAwMDAwMDAwMDAwMDAwMDAiLCJhdmFpbGFibGUiOiIwIn0=" }, "tokens_burnt": "425897740543400000000" }, "proof": [] }, { "block_hash": "J6YXMnLPfLEPyvL3fWdhWPzpWAeW8zNY2CwAFwAg9tfr", "id": "7neJYE45vXnQia1LQqWuAfyTRXHy4vv88JaULa5DnNBd", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 223182562500, "logs": [], "receipt_ids": [ "2c59u2zYj41JuhMfPUCKjNucmYfz2Jt83JLWP6VyQn1S" ], "status": { "SuccessValue": "" }, "tokens_burnt": "22318256250000000000" }, "proof": [ { "direction": "Right", "hash": "D6tGfHwKh21PqzhBTsdKCsTtZvXFDkmH39dQiQBoGN3w" } ] }, { "block_hash": "HRRF7N1PphZ46eN5g4DjqEgYHBYM76yGiXSTYWsfMGoy", "id": "2c59u2zYj41JuhMfPUCKjNucmYfz2Jt83JLWP6VyQn1S", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [] }, { "block_hash": "J6YXMnLPfLEPyvL3fWdhWPzpWAeW8zNY2CwAFwAg9tfr", "id": "kaYatRKxcC1NXac69WwTqg6K13oXq2yEuy4LLZtsV2G", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Left", "hash": "1uVJZ8vNQpHMwPA38DYJjk9PpvnHDhsDcMxrTXcwf1s" } ] } ], "status": { "SuccessValue": "eyJ0b3RhbCI6IjEyNTAwMDAwMDAwMDAwMDAwMDAwMDAiLCJhdmFpbGFibGUiOiIwIn0=" }, "transaction": { "actions": [ { "FunctionCall": { "args": "e30=", "deposit": "125000000000000000000000", "gas": 100000000000000, "method_name": "storage_deposit" } } ], "hash": "6sDUF1f5hebpybUbipNJer5ez13EY4HW1VBJEBqZjCEm", "nonce": 47589658000011, "public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN", "receiver_id": "ft.demo.testnet", "signature": "ed25519:31PfuinsVvM1o2CmiZbSguFkZKYqAtkf5PHerfexhDbC3SsJWDzRFBpoUYTNDJddhKeqs93GQ3SHtUyqaSYhhQ9X", "signer_id": "serhii.testnet" }, "transaction_outcome": { "block_hash": "5XiuxzTpyw6p2NTD5AqrZYNW7SHvpj8MhUCyn1x2KSQR", "id": "6sDUF1f5hebpybUbipNJer5ez13EY4HW1VBJEBqZjCEm", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 2427959010878, "logs": [], "receipt_ids": [ "4urgFabknn1myZkjTYdb1BFSoEimP21k9smCUWoSggA7" ], "status": { "SuccessReceiptId": "4urgFabknn1myZkjTYdb1BFSoEimP21k9smCUWoSggA7" }, "tokens_burnt": "242795901087800000000" }, "proof": [] } } } ```
Transfer the tokens: - using NEAR CLI: ```bash near contract call-function as-transaction ft_transfer json-args '{"receiver_id": "", "amount": "1"}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as network-config testnet sign-with-keychain send ``` Result example: ```bash Scheduling a call: berryclub.ek.near.ft_transfer({"receiver_id": "volovyk.near", "amount": "1"}) with attached 0.000000000000000000000001 NEAR Receipt: GDeE3Kv1JHgs71A22NEUbgq55r2Hvcnis8gCMyJtQ2mx Log [berryclub.ek.near]: Transfer 1 from serhii.near to volovyk.near Transaction Id 3MkWKbXVP8wyy4pBofELqiE1pwx7ie2v3SKCwaobNcEe To see the transaction in the transaction explorer, please open this url in your browser https://nearblocks.io/txns/3MkWKbXVP8wyy4pBofELqiE1pwx7ie2v3SKCwaobNcEe '' ``` - with JSON RPC call: Transaction representation: ```yaml Transaction: { block_hash: `456…abc`, signer_id: "serhii.near", public_key: "ed25519:789…def", nonce: 123, receiver_id: "berryclub.ek.near", actions: [ FunctionCall( FunctionCallAction { method_name: ft_transfer, args: `{"receiver_id": "volovyk.near", "amount": "1"}`, gas: 300000000000000, deposit: 1, }, ), ] } ``` ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=broadcast_tx_commit \ params:='["CwAAAHNlcmhpaS5uZWFyAAmQpgZcJM5nMc6f3tqmw/YI4eAvc84ZgsKMRRRzhY/6CQAAAAAAAAARAAAAYmVycnljbHViLmVrLm5lYXLLWPIiUOElkDF3u4hLAMJ0Sjeo1V338pDdHIp70va3ewEAAAACCwAAAGZ0X3RyYW5zZmVyKwAAAHsicmVjZWl2ZXJfaWQiOiJ2b2xvdnlrLm5lYXIiLCJhbW91bnQiOiIxIn0AQHoQ81oAAAEAAAAAAAAAAAAAAAAAAAAA7fDOZQt3zCtdS05Y8XaZFlwO/Gd5wkkNAHShzDiLQXk4Q4ixpraLPMJivs35PZD0gocXl1iGFbQ46NG3VllzCA=="]' ``` To get details of this transaction: ```bash http post https://archival-rpc.mainnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status \ params:='["2Fy4714idMCoja7QLdGAbQZHzV2XEnUdwZX6yGa46VMX", "serhii.near"]' id=myid ```
**Example Response:** ```json { "id": "myid", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "serhii.near", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6InZvbG92eWsubmVhciIsImFtb3VudCI6IjEifQ==", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.near", "signer_public_key": "ed25519:eLbduR3uJGaAHLXeGKEfo1fYmYFKkLyR1R8ZPCxrJAM" } }, "receipt_id": "ExhYcvwAUb3Jpm38pSQ5oobwJAouBqqDZjbhavKrZtur", "receiver_id": "berryclub.ek.near" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "18418055677558685763688" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.near", "signer_public_key": "ed25519:eLbduR3uJGaAHLXeGKEfo1fYmYFKkLyR1R8ZPCxrJAM" } }, "receipt_id": "EAPh8XrBMqm6iuVH5jsfemz4YqUxWsV8Mz241cw5tjvE", "receiver_id": "serhii.near" } ], "receipts_outcome": [ { "block_hash": "6Re4NTkKzD7maKx3MuoxzYVHQKqjgnXW8rNjGjeVx8YC", "id": "ExhYcvwAUb3Jpm38pSQ5oobwJAouBqqDZjbhavKrZtur", "outcome": { "executor_id": "berryclub.ek.near", "gas_burnt": 6365774114160, "logs": [ "Transfer 1 from serhii.near to volovyk.near" ], "receipt_ids": [ "EAPh8XrBMqm6iuVH5jsfemz4YqUxWsV8Mz241cw5tjvE" ], "status": { "SuccessValue": "" }, "tokens_burnt": "636577411416000000000" }, "proof": [ { "direction": "Left", "hash": "2eUmWnLExsH5jb6mALY9jTC8FiQH4FcuxQ16tn7RfkYr" }, { "direction": "Right", "hash": "266d5QfDKXNbAWJgXMJXgLP97VwoMiC4Qyt8wH7xcs1Q" }, { "direction": "Right", "hash": "EkJAuJigdVSZj41yGXSZYAtDV7Xwe2Hv9Xsqcv6LUZvq" } ] }, { "block_hash": "3XMoeEdm1zE64aByFuWCrZaxfbvsjMHRFcL8Wsp95vyt", "id": "EAPh8XrBMqm6iuVH5jsfemz4YqUxWsV8Mz241cw5tjvE", "outcome": { "executor_id": "serhii.near", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Right", "hash": "EGC9ZPJHTbmCs3aQDuCkFQboGLBxU2uzrSZMsp8WonDu" }, { "direction": "Right", "hash": "EsBd1n7bDAphA3HY84DrrKd1GP1VugeNiqFCET2S5sNG" }, { "direction": "Left", "hash": "H4q3ByfNB7QH9QEuHN3tcGay7tjhsZwjXx3sq3Vm3Lza" } ] } ], "status": { "SuccessValue": "" }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6InZvbG92eWsubmVhciIsImFtb3VudCI6IjEifQ==", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer" } } ], "hash": "2Fy4714idMCoja7QLdGAbQZHzV2XEnUdwZX6yGa46VMX", "nonce": 10, "public_key": "ed25519:eLbduR3uJGaAHLXeGKEfo1fYmYFKkLyR1R8ZPCxrJAM", "receiver_id": "berryclub.ek.near", "signature": "ed25519:5eJPGirNkBUbMeyRfEA4fgi1FtkgGk8pmbbkmiz3Faf6zrANpBsCs5bZd5heSTvQ6b3fEPLSPCPi2iwD2XJT93As", "signer_id": "serhii.near" }, "transaction_outcome": { "block_hash": "EAcwavyaeNWZnfhYP2nAWzeDgMiuiyRHfaprFqhXgCRF", "id": "2Fy4714idMCoja7QLdGAbQZHzV2XEnUdwZX6yGa46VMX", "outcome": { "executor_id": "serhii.near", "gas_burnt": 2428041740436, "logs": [], "receipt_ids": [ "ExhYcvwAUb3Jpm38pSQ5oobwJAouBqqDZjbhavKrZtur" ], "status": { "SuccessReceiptId": "ExhYcvwAUb3Jpm38pSQ5oobwJAouBqqDZjbhavKrZtur" }, "tokens_burnt": "242804174043600000000" }, "proof": [ { "direction": "Right", "hash": "GatQmy7fW5uXRJRSg7A315CWzWWcQCGk4GJXyW3cjw4j" }, { "direction": "Right", "hash": "89WJwAetivZLvAkVLXUt862o7zJX7YYt6ZixdWebq3xv" }, { "direction": "Right", "hash": "CH3wHSqYPJp35krLjSgJTgCFYnv1ymhd9bJpjXA31VVD" } ] } } } ```
You can get the same info later by the transaction hash from the previous call: - using NEAR Explorer: https://nearblocks.io - with JSON RPC call ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=EXPERIMENTAL_tx_status \ params:='[ "2Fy4714idMCoja7QLdGAbQZHzV2XEnUdwZX6yGa46VMX", "sender.testnet"]' ``` Let's create test transaction that should fail and investigate the response. We will try to send more tokens that are available on this account: - using NEAR CLI: ```bash near contract call-function as-transaction ft_transfer json-args '{"receiver_id": "", "amount": "1"}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as network-config testnet sign-with-keychain send ``` - with JSON RPC call: Transaction representation: ```yaml Transaction: { block_hash: `456…abc`, signer_id: "serhii.near", public_key: "ed25519:789…def", nonce: 123, receiver_id: "berryclub.ek.near", actions: [ FunctionCall( FunctionCallAction { method_name: ft_transfer, args: `{"receiver_id":"volovyk.near","amount":"10000000000000000000"}`, gas: 300000000000000, deposit: 1, }, ), ] } ``` ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=broadcast_tx_commit \ params:='["DgAAAHNlcmhpaS50ZXN0bmV0AEKEp54fyVkp8dJE2l/m1ErjdhDGodBK8ZF6JLeHFMeZofqoVEgrAAAgAAAAZGV2LTE2MjMzMzM3OTU2MjMtMjEzOTk5NTk3NzgxNTm8Xq8BTIi6utG0424Gg7CknYzLH8RH/A409jq5o0zi7gEAAAACCwAAAGZ0X3RyYW5zZmVyPwAAAHsicmVjZWl2ZXJfaWQiOiJkZXYtMTYyMzMzMzkxNjM2OC01ODcwNzQzNDg3ODUzMyIsImFtb3VudCI6IjEifQBAehDzWgAAAQAAAAAAAAAAAAAAAAAAAABCwjqayKdpWgM6PE0ixzm/Gy0EtdpxVn0xehMTBReVfVAKIBTDPoPSaOdT8fAhk343F5uOMfSijhTqU2mWV3oD"]' ``` To get details of this transaction: ```bash http post https://archival-rpc.mainnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status \ params:='["CKHzodHvFw4C87PazsniycYZZHm37CEWLE2u8VUUMU7r", "serhii.near"]' id=myid ```
**Example Response:** ```json { "id": "myid", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "serhii.near", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6InZvbG92eWsubmVhciIsImFtb3VudCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.near", "signer_public_key": "ed25519:eLbduR3uJGaAHLXeGKEfo1fYmYFKkLyR1R8ZPCxrJAM" } }, "receipt_id": "5bdBKwS1RH7wm8eoG6ZeESdhNpj9HffUcf8RoP6Ng5d", "receiver_id": "berryclub.ek.near" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "1" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "system", "signer_public_key": "ed25519:11111111111111111111111111111111" } }, "receipt_id": "Td3QxpKhMdi8bfVeMiQZwNS1VzPXceQdn6xdftoC8k6", "receiver_id": "serhii.near" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "18653463364152698495356" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.near", "signer_public_key": "ed25519:eLbduR3uJGaAHLXeGKEfo1fYmYFKkLyR1R8ZPCxrJAM" } }, "receipt_id": "DwLMVTdqv9Z4g9QC4AthTXHqqeJVAH4s1tFXHQYMArW7", "receiver_id": "serhii.near" } ], "receipts_outcome": [ { "block_hash": "DTruWLgm5Y56yDrxUipvYqKKm8F7hxVQTarNQqe147zs", "id": "5bdBKwS1RH7wm8eoG6ZeESdhNpj9HffUcf8RoP6Ng5d", "outcome": { "executor_id": "berryclub.ek.near", "gas_burnt": 4011776278642, "logs": [], "receipt_ids": [ "Td3QxpKhMdi8bfVeMiQZwNS1VzPXceQdn6xdftoC8k6", "DwLMVTdqv9Z4g9QC4AthTXHqqeJVAH4s1tFXHQYMArW7" ], "status": { "Failure": { "ActionError": { "index": 0, "kind": { "FunctionCallError": { "ExecutionError": "Smart contract panicked: The account doesn't have enough balance" } } } } }, "tokens_burnt": "401177627864200000000" }, "proof": [ { "direction": "Right", "hash": "6GHrA42oMEF4g7YCBpPw9EakkLiepTHnQBvaKtmsenEY" }, { "direction": "Right", "hash": "DCG3qZAzf415twXfHmgBUdB129g2iZoQ4v8dawwBzhSh" } ] }, { "block_hash": "F9xNWGhJuYW336f3qVaDDAipsyfpudJHYbmt5in3MeMT", "id": "Td3QxpKhMdi8bfVeMiQZwNS1VzPXceQdn6xdftoC8k6", "outcome": { "executor_id": "serhii.near", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Right", "hash": "CJNvis1CoJmccshDpPBrk3a7fdZ5HnMQuy3p2Kd2GCdS" }, { "direction": "Left", "hash": "4vHM3fbdNwXGMp9uYzVKB13abEM6qdPUuZ9rfrdsaDzc" } ] }, { "block_hash": "F9xNWGhJuYW336f3qVaDDAipsyfpudJHYbmt5in3MeMT", "id": "DwLMVTdqv9Z4g9QC4AthTXHqqeJVAH4s1tFXHQYMArW7", "outcome": { "executor_id": "serhii.near", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Left", "hash": "BR3R7tjziEgXMiHaJ7VuuXCo2yBHB2ZzsoxobPhPjFeJ" }, { "direction": "Left", "hash": "4vHM3fbdNwXGMp9uYzVKB13abEM6qdPUuZ9rfrdsaDzc" } ] } ], "status": { "Failure": { "ActionError": { "index": 0, "kind": { "FunctionCallError": { "ExecutionError": "Smart contract panicked: The account doesn't have enough balance" } } } } }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6InZvbG92eWsubmVhciIsImFtb3VudCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAwIn0=", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer" } } ], "hash": "CKHzodHvFw4C87PazsniycYZZHm37CEWLE2u8VUUMU7r", "nonce": 12, "public_key": "ed25519:eLbduR3uJGaAHLXeGKEfo1fYmYFKkLyR1R8ZPCxrJAM", "receiver_id": "berryclub.ek.near", "signature": "ed25519:63MC3f8m5jeycpy97G9XaCwmJLx4YHRn2x5AEJDiYYzZ3TzdzWsrz8dgaz2kHR2jsWh35aZoL97tw1RRTHK6ZQYq", "signer_id": "serhii.near" }, "transaction_outcome": { "block_hash": "7YUgyBHgmbGy1edhaWRZeBVq9zzbnzrRGtVRQS5PpooW", "id": "CKHzodHvFw4C87PazsniycYZZHm37CEWLE2u8VUUMU7r", "outcome": { "executor_id": "serhii.near", "gas_burnt": 2428084223182, "logs": [], "receipt_ids": [ "5bdBKwS1RH7wm8eoG6ZeESdhNpj9HffUcf8RoP6Ng5d" ], "status": { "SuccessReceiptId": "5bdBKwS1RH7wm8eoG6ZeESdhNpj9HffUcf8RoP6Ng5d" }, "tokens_burnt": "242808422318200000000" }, "proof": [ { "direction": "Right", "hash": "Agyg5P46kSVa4ptG9spteHpZ5c8XkvfbmDN5EUXhC1Wr" }, { "direction": "Right", "hash": "3JDKkLCy5bJaAU3exa66sotTwJyGwyChxeNJgKReKw34" }, { "direction": "Right", "hash": "7GXEmeQEJdd4c2kgN7NoYiF2bkjzV4bNkMmkhpK14NTz" } ] } } } ```
Was the fungible token transfer successful? - Look for `result` » `transaction_outcome` » `outcome` » see if `SuccessReceiptId` is a key - if `SuccessReceiptId` is not a key, this fungible token transfer has `failed`. - If it does have that key, get the value, which is a `receipt ID` - Loop through `result` » `receipts_outcome` until you find an object that ID (from above) under the id key - in that object check `outcome` » `status` » (see if SuccessValue is a key) - If SuccessValue is a key, fungible token transfer succeeded, if not, it failed. To determine how many fungible tokens were transferred, look at: - `result` » `transaction` » `actions` » `FunctionCall` » `args` - then take the args and `base64` decode it, that will give you a JSON payload and look for the `amount` key - It will contain a stringified number that represents the number of fungible tokens that were successfully transferred ## Transfer and call {#transfer-and-call} If the idea of a fungible token using "transfer and call" is new, please review the comments above the function in [the Nomicon spec](https://github.com/near/NEPs/tree/master/neps/nep-0141.md#reference-level-explanation). Also, see a similar idea [from EIP-677](https://github.com/ethereum/EIPs/issues/677). For this example we will build and deploy FT contracts from [near-sdk-rs/examples/fungible-token](https://github.com/near/near-sdk-rs/tree/master/examples/fungible-token). Let's call `ft_transfer_call` function on `ft` contract (receiver) and examine successful and unsuccessful scenarios. ### Successful transfer and call {#successful-transfer-and-call} Let's send 10 N to `DEFI` contract that requires only 9 N. - using NEAR CLI ```bash near contract call-function as-transaction ft_transfer_call json-args '{"receiver_id": "", "amount": "10", "msg": "take-my-money"}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as network-config testnet sign-with-keychain send ``` - with JSON RPC call Transaction representation: ```yaml Transaction: { block_hash: `456…abc`, signer_id: "serhii.testnet", public_key: "ed25519:789…def", nonce: 123, receiver_id: "dev-1623333795623-21399959778159", actions: [ FunctionCall( FunctionCallAction { method_name: ft_transfer_call, args: `{"receiver_id":"dev-1623693121955-71667632531176","amount":"10","msg":"take-my-money"}`, gas: 300000000000000, deposit: 1, }, ), ] } ``` ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=broadcast_tx_commit \ params:='["DgAAAHNlcmhpaS50ZXN0bmV0AEKEp54fyVkp8dJE2l/m1ErjdhDGodBK8ZF6JLeHFMeZqPqoVEgrAAAgAAAAZGV2LTE2MjMzMzM3OTU2MjMtMjEzOTk5NTk3NzgxNTn9j4g2IJ8nGQ38i3+k+4WBAeJL1xP7ygQhC7CrvEG4NQEAAAACEAAAAGZ0X3RyYW5zZmVyX2NhbGxWAAAAeyJyZWNlaXZlcl9pZCI6ImRldi0xNjIzNjkzMTIxOTU1LTcxNjY3NjMyNTMxMTc2IiwiYW1vdW50IjoiMTAiLCJtc2ciOiJ0YWtlLW15LW1vbmV5In0AQHoQ81oAAAEAAAAAAAAAAAAAAAAAAAAANY2lHqJlAJYNDGEQiUNnmfiBV44Q1sdg45xNlNvlROOM+AtN1z3PSJqM6M6jAKXUwANoQTzFqXhIMHIjIPbTAA=="]' ``` To get details of this transaction: ```bash http post https://archival-rpc.testnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status \ params:='["5n1kwA3TQQyFTkddR2Jau3H1Pt8ebQNGaov6aCQ6TDp1", "serhii.testnet"]' id=myid ```
**Example Response:** ```json { "id": "myid", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "serhii.testnet", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6ImRldi0xNjIzNjkzMTIxOTU1LTcxNjY3NjMyNTMxMTc2IiwiYW1vdW50IjoiMTAiLCJtc2ciOiJ0YWtlLW15LW1vbmV5In0=", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer_call" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "Hw6z8kJ7CSaC6SgyQzcmXzNX9gq1gaAnLS169qgyZ2Vk", "receiver_id": "dev-1623333795623-21399959778159" }, { "predecessor_id": "dev-1623333795623-21399959778159", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJzZW5kZXJfaWQiOiJzZXJoaWkudGVzdG5ldCIsImFtb3VudCI6IjEwIiwibXNnIjoidGFrZS1teS1tb25leSJ9", "deposit": "0", "gas": 70000000000000, "method_name": "ft_on_transfer" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [ { "data_id": "EiDQi54XHfdD1KEcgiNzogXxXuwTpzeQfmyqVwbq7H4D", "receiver_id": "dev-1623333795623-21399959778159" } ], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "EB69xtJiLRh9RNzAHgBGmom8551hrK2xSRreqbjvJgu5", "receiver_id": "dev-1623693121955-71667632531176" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "13116953530949529501760" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "AkwgvxUspRgy255fef2hrEWMbrMWFtnTRGduSgDRdSW1", "receiver_id": "serhii.testnet" }, { "predecessor_id": "dev-1623333795623-21399959778159", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJzZW5kZXJfaWQiOiJzZXJoaWkudGVzdG5ldCIsInJlY2VpdmVyX2lkIjoiZGV2LTE2MjM2OTMxMjE5NTUtNzE2Njc2MzI1MzExNzYiLCJhbW91bnQiOiIxMCJ9", "deposit": "0", "gas": 5000000000000, "method_name": "ft_resolve_transfer" } } ], "gas_price": "186029458", "input_data_ids": [ "EiDQi54XHfdD1KEcgiNzogXxXuwTpzeQfmyqVwbq7H4D" ], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "4Tc8MsrJZSMpNZx7u4jSqxr3WhRzqxaNHxLJFqz8tUPR", "receiver_id": "dev-1623333795623-21399959778159" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "761030677610514102464" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "9rxcC9o8x4RX7ftsDCfxK8qnisYv45rA1HGPxhuukWUL", "receiver_id": "serhii.testnet" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "2137766093631769060520" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "H7YWFkvx16Efy2keCQ7BQ67BMEsdgdYLqJ99G4H3dR1D", "receiver_id": "serhii.testnet" } ], "receipts_outcome": [ { "block_hash": "B9yZz1w3yzrqQnfBFAf17S4TLaHakXJWqmFDBbFxaEiZ", "id": "Hw6z8kJ7CSaC6SgyQzcmXzNX9gq1gaAnLS169qgyZ2Vk", "outcome": { "executor_id": "dev-1623333795623-21399959778159", "gas_burnt": 20612680932083, "logs": [ "Transfer 10 from serhii.testnet to dev-1623693121955-71667632531176" ], "receipt_ids": [ "EB69xtJiLRh9RNzAHgBGmom8551hrK2xSRreqbjvJgu5", "4Tc8MsrJZSMpNZx7u4jSqxr3WhRzqxaNHxLJFqz8tUPR", "H7YWFkvx16Efy2keCQ7BQ67BMEsdgdYLqJ99G4H3dR1D" ], "status": { "SuccessReceiptId": "4Tc8MsrJZSMpNZx7u4jSqxr3WhRzqxaNHxLJFqz8tUPR" }, "tokens_burnt": "2061268093208300000000" }, "proof": [] }, { "block_hash": "7Z4LHWksvw7sKYKwpQfjEMG8oigjtRXKa3EopN7hS2v7", "id": "EB69xtJiLRh9RNzAHgBGmom8551hrK2xSRreqbjvJgu5", "outcome": { "executor_id": "dev-1623693121955-71667632531176", "gas_burnt": 3568066327145, "logs": [ "Sender @serhii.testnet is transferring 10 tokens using ft_on_transfer, msg = take-my-money" ], "receipt_ids": [ "AkwgvxUspRgy255fef2hrEWMbrMWFtnTRGduSgDRdSW1" ], "status": { "SuccessValue": "IjEi" }, "tokens_burnt": "356806632714500000000" }, "proof": [ { "direction": "Right", "hash": "5X2agUKpqmk7QkUZsDQ4R4HdX7zXeuPYpVAfvbmF5Gav" } ] }, { "block_hash": "CrSDhQNn72K2Qr1mmoM9j3YHCo3wfZdmHjpHJs74WnPk", "id": "AkwgvxUspRgy255fef2hrEWMbrMWFtnTRGduSgDRdSW1", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Right", "hash": "4WG6hF5fTAtM7GSqU8mprrFwRVbChGMCV2NPZEjEdnc1" } ] }, { "block_hash": "CrSDhQNn72K2Qr1mmoM9j3YHCo3wfZdmHjpHJs74WnPk", "id": "4Tc8MsrJZSMpNZx7u4jSqxr3WhRzqxaNHxLJFqz8tUPR", "outcome": { "executor_id": "dev-1623333795623-21399959778159", "gas_burnt": 6208280264404, "logs": [ "Refund 1 from dev-1623693121955-71667632531176 to serhii.testnet" ], "receipt_ids": [ "9rxcC9o8x4RX7ftsDCfxK8qnisYv45rA1HGPxhuukWUL" ], "status": { "SuccessValue": "Ijki" }, "tokens_burnt": "620828026440400000000" }, "proof": [ { "direction": "Left", "hash": "BzT8YhEDDWSuoGGTBzH2Cj5GC4c56uAQxk41by4KVnXi" } ] }, { "block_hash": "3Q2Zyscj6vG5nC2vdoYfcBHU9RVaAwoxHsHzAKVcAHZ6", "id": "9rxcC9o8x4RX7ftsDCfxK8qnisYv45rA1HGPxhuukWUL", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [] }, { "block_hash": "7Z4LHWksvw7sKYKwpQfjEMG8oigjtRXKa3EopN7hS2v7", "id": "H7YWFkvx16Efy2keCQ7BQ67BMEsdgdYLqJ99G4H3dR1D", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Left", "hash": "61ak42D3duBBunCz3w4xXxoEeR2N7oav5e938TnmGFGN" } ] } ], "status": { "SuccessValue": "Ijki" }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6ImRldi0xNjIzNjkzMTIxOTU1LTcxNjY3NjMyNTMxMTc2IiwiYW1vdW50IjoiMTAiLCJtc2ciOiJ0YWtlLW15LW1vbmV5In0=", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer_call" } } ], "hash": "5n1kwA3TQQyFTkddR2Jau3H1Pt8ebQNGaov6aCQ6TDp1", "nonce": 47589658000040, "public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN", "receiver_id": "dev-1623333795623-21399959778159", "signature": "ed25519:256qp2jAGXhhw2t2XfUAjWwzz3XcD83DH2v9THwDPsZjCLWHU8QJd6cuA773NP9yBmTd2ZyYiFHuxVEkYqnbsaSb", "signer_id": "serhii.testnet" }, "transaction_outcome": { "block_hash": "96k8kKzFuZWxyiUnT774Rg7DC3XDZNuxhxD1qEViFupd", "id": "5n1kwA3TQQyFTkddR2Jau3H1Pt8ebQNGaov6aCQ6TDp1", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 2428149065268, "logs": [], "receipt_ids": [ "Hw6z8kJ7CSaC6SgyQzcmXzNX9gq1gaAnLS169qgyZ2Vk" ], "status": { "SuccessReceiptId": "Hw6z8kJ7CSaC6SgyQzcmXzNX9gq1gaAnLS169qgyZ2Vk" }, "tokens_burnt": "242814906526800000000" }, "proof": [] } } } ```
Now, let's try to follow the steps described in the previous section and determine if these transactions was successful. In addition to being successful, let's analyze the various receipts in the series of cross-contract calls to determine how many fungible tokens were transferred. This will be the most complex case we'll look at. 1. Check that `result` » `transaction_outcome` » `outcome` » `status` has `SuccessReceiptId` as a key. If not, no fungible tokens were transferred. 2. Take the value of the `SuccessReceiptId` key. In the case above it's `Hw6z8kJ7CSaC6SgyQzcmXzNX9gq1gaAnLS169qgyZ2Vk`. 3. Now, under `result` » `receipts` loop through the array until you find a receipt where the `receipt_id` matches the value from step 2. (Note that in the receipt, under `Actions` there's an element mentioning calling the `method_name: "ft_transfer_call"`.) On the same level of JSON, there's an `args` key. That's a base64-encoded value of the arguments passed to the method. When decoded it is: ```json {"receiver_id":"dev-1623693121955-71667632531176","amount":"10","msg":"take-my-money"} ``` 4. Loop through `result` » `receipts_outcome` until finding the object where `id` is equal to the value from step 2. Similar to step 1, this object will also contain a `status` field that should contain the key `SuccessReceiptId`. Again, if this isn't there no fungible tokens were transferred, otherwise get the value of the `SuccessReceiptId`. In the above example, this value is `4Tc8MsrJZSMpNZx7u4jSqxr3WhRzqxaNHxLJFqz8tUPR`. 5. Similar to the previous step, loop through the `result` » `receipts_outcome` until you find the object where the `id` matches the value from step 4. In that object check that `outcome` » `status` has the `SuccessValue` field. This `SuccessValue` represents how many fungible tokens the receiving contract is "returning" to the fungible token contract. Note that in the example above the value is `Ijki`, which is the base64-encoded version of `"9"`. At this point, we know that 10 fungible tokens were sent (from step 3) and 9 were taken. For additional clarity, let's take a look at one more optional aspect. In step 4 we isolated an object in `result` » `receipts_outcome`. There's an array of `receipt_ids` that's particularly interesting. The first element in the array is the receipt ID `EB69xtJiLRh9RNzAHgBGmom8551hrK2xSRreqbjvJgu5`. If we loop through the `result` » `receipts_outcome` and find this as the value for the `id` key, we'll see what happened in the function `ft_on_transfer` which takes place in the contract receiving the fungible tokens. In this object the `status` » `SuccessValue` is `IjEi` which is the base64-encoded value of `"1"`. In summary: 1. A user called the fungible token contract with the method `ft_transfer_call` specifying the receiver account, how many tokens to send, and custom info. 2. The receiver account implemented `ft_on_transfer`, returning `"1"` to the callback function on the fungible token contract. 3. The fungible token contract's callback is `ft_resolve_transfer` and receives this value of `"1"`. It knows that 1 token was returned, so subtracts that from the 10 it intended to send. It then returns to the user how many tokens were used in this back-and-forth series of cross-contract calls: `"9"`. ### Failed transfer and call {#failed-transfer-and-call} Let's try to send more tokens than the account has: - using NEAR CLI ```bash near contract call-function as-transaction ft_transfer_call json-args '{"receiver_id": "", "amount": "1000000000", "msg": "take-my-money"}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as network-config testnet sign-with-keychain send ``` Transaction representation: ```yaml Transaction: { block_hash: `456…abc`, signer_id: "serhii.testnet", public_key: "ed25519:789…def", nonce: 123, receiver_id: "dev-1623333795623-21399959778159", actions: [ FunctionCall( FunctionCallAction { method_name: ft_transfer_call, args: `{"receiver_id":"dev-1623333916368-58707434878533","amount":"1000000000","msg":"take-my-money"}`, gas: 300000000000000, deposit: 1, }, ), ] } ``` - with JSON RPC call ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=broadcast_tx_commit \ params:='["DgAAAHNlcmhpaS50ZXN0bmV0AEKEp54fyVkp8dJE2l/m1ErjdhDGodBK8ZF6JLeHFMeZn/qoVEgrAAAgAAAAZGV2LTE2MjMzMzM3OTU2MjMtMjEzOTk5NTk3NzgxNTnrbOQ93Wv9xxBwmq4yDYrssCpwKSI2bzjNNCCCHMZKNwEAAAACEAAAAGZ0X3RyYW5zZmVyX2NhbGxeAAAAeyJyZWNlaXZlcl9pZCI6ImRldi0xNjIzMzMzOTE2MzY4LTU4NzA3NDM0ODc4NTMzIiwiYW1vdW50IjoiMTAwMDAwMDAwMCIsIm1zZyI6InRha2UtbXktbW9uZXkifQBAehDzWgAAAQAAAAAAAAAAAAAAAAAAAABQh3k+7zG2m/Yz3O/FBrvLaBwR/5YRB5FbFnb27Nfu6BW/Wh77RFH7+ktBwGLBwFbJGxiumIcsqBiGXgg1EPMN"]' ``` To get details of this transaction: ```bash http post https://archival-rpc.testnet.near.org jsonrpc=2.0 method=EXPERIMENTAL_tx_status \ params:='["FQsh44pvEsK8RS9AbK868CmGwfhUU2pUrizkQ6wCWTsB", "serhii.testnet"]' id=myid ```
**Example response**: ```json { "id": "myid", "jsonrpc": "2.0", "result": { "receipts": [ { "predecessor_id": "serhii.testnet", "receipt": { "Action": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6ImRldi0xNjIzMzMzOTE2MzY4LTU4NzA3NDM0ODc4NTMzIiwiYW1vdW50IjoiMTAwMDAwMDAwMCIsIm1zZyI6InRha2UtbXktbW9uZXkifQ==", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer_call" } } ], "gas_price": "186029458", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "83AdQ16bpAC7BEUyF7zoRsAgeNW7HHmjhZLvytEsrygo", "receiver_id": "dev-1623333795623-21399959778159" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "1" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "system", "signer_public_key": "ed25519:11111111111111111111111111111111" } }, "receipt_id": "Euy4Q33DfvJTXD8HirE5ACoXnw9PMTQ2Hq47aGyD1spc", "receiver_id": "serhii.testnet" }, { "predecessor_id": "system", "receipt": { "Action": { "actions": [ { "Transfer": { "deposit": "18681184841157733814920" } } ], "gas_price": "0", "input_data_ids": [], "output_data_receivers": [], "signer_id": "serhii.testnet", "signer_public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN" } }, "receipt_id": "6ZDoSeV3gLFS2NXqMCJGEUR3VwBpSxBEPjnEEaAQfmXL", "receiver_id": "serhii.testnet" } ], "receipts_outcome": [ { "block_hash": "BohRBwqjRHssDVS9Gt9dj3SYuipxHA81xXFjRVLqgGeb", "id": "83AdQ16bpAC7BEUyF7zoRsAgeNW7HHmjhZLvytEsrygo", "outcome": { "executor_id": "dev-1623333795623-21399959778159", "gas_burnt": 3734715409940, "logs": [], "receipt_ids": [ "Euy4Q33DfvJTXD8HirE5ACoXnw9PMTQ2Hq47aGyD1spc", "6ZDoSeV3gLFS2NXqMCJGEUR3VwBpSxBEPjnEEaAQfmXL" ], "status": { "Failure": { "ActionError": { "index": 0, "kind": { "FunctionCallError": { "ExecutionError": "Smart contract panicked: The account doesn't have enough balance" } } } } }, "tokens_burnt": "373471540994000000000" }, "proof": [] }, { "block_hash": "4BzTmMmTjKvfs6ANS5gmJ6GQzhqianEGWq7SaxSfPbdC", "id": "Euy4Q33DfvJTXD8HirE5ACoXnw9PMTQ2Hq47aGyD1spc", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Right", "hash": "5ipmcdgTieQqFXWQFCwcbZhFtkHE4PL4nW3mknBchpG6" } ] }, { "block_hash": "4BzTmMmTjKvfs6ANS5gmJ6GQzhqianEGWq7SaxSfPbdC", "id": "6ZDoSeV3gLFS2NXqMCJGEUR3VwBpSxBEPjnEEaAQfmXL", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 0, "logs": [], "receipt_ids": [], "status": { "SuccessValue": "" }, "tokens_burnt": "0" }, "proof": [ { "direction": "Left", "hash": "9tcjij6M8Ge4aJcAa97He5nw8pH7PF8ZjRHVahBZD2VW" } ] } ], "status": { "Failure": { "ActionError": { "index": 0, "kind": { "FunctionCallError": { "ExecutionError": "Smart contract panicked: The account doesn't have enough balance" } } } } }, "transaction": { "actions": [ { "FunctionCall": { "args": "eyJyZWNlaXZlcl9pZCI6ImRldi0xNjIzMzMzOTE2MzY4LTU4NzA3NDM0ODc4NTMzIiwiYW1vdW50IjoiMTAwMDAwMDAwMCIsIm1zZyI6InRha2UtbXktbW9uZXkifQ==", "deposit": "1", "gas": 100000000000000, "method_name": "ft_transfer_call" } } ], "hash": "FQsh44pvEsK8RS9AbK868CmGwfhUU2pUrizkQ6wCWTsB", "nonce": 47589658000031, "public_key": "ed25519:5UfEFyve3RdqKkWtALMreA9jzsAGDgCtwEXGNtkGeruN", "receiver_id": "dev-1623333795623-21399959778159", "signature": "ed25519:2cPASnxKtCoQtZ9NFq63fg8RzpjvmmE8hL4s2jk8zuhnBCD3AnYQ6chZZrUBGwu7WrsGuWUyohP1bEca4vfbsorC", "signer_id": "serhii.testnet" }, "transaction_outcome": { "block_hash": "FwHUeqmYpvgkL7eBrUUAEMCuaQshcSy5vm4AHchebhK1", "id": "FQsh44pvEsK8RS9AbK868CmGwfhUU2pUrizkQ6wCWTsB", "outcome": { "executor_id": "serhii.testnet", "gas_burnt": 2428166952740, "logs": [], "receipt_ids": [ "83AdQ16bpAC7BEUyF7zoRsAgeNW7HHmjhZLvytEsrygo" ], "status": { "SuccessReceiptId": "83AdQ16bpAC7BEUyF7zoRsAgeNW7HHmjhZLvytEsrygo" }, "tokens_burnt": "242816695274000000000" }, "proof": [] } } } ```
Let's examine this response. * `result` » `transaction_outcome` » `outcome` » `status` » `SuccessReceiptId` is `83AdQ16bpAC7BEUyF7zoRsAgeNW7HHmjhZLvytEsrygo` * check `result` » `receipts_outcome` » `0` » `outcome` » `status` and find `Failure` status there :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/api/rpc/gas.md # Source: https://docs.near.org/protocol/gas.md --- id: gas title: Gas (Execution Fees) description: "Learn about NEAR's gas system - execution fees that prevent spam, incentivize developers with 30% of burned gas, and use deterministic gas units with dynamic pricing." --- This section explains how gas works in the NEAR Protocol, including how it is calculated, charged, and used to incentivize developers. On every transaction the NEAR network charges a tiny fee known as **gas**. This fee is a simple mechanism that allows us to: 1. **Prevent** bad actors from **spamming** the network with useless transactions 2. **Burn** a minuscule fraction of the **token supply** on each transaction 3. **Incentivize developers** by giving contracts 30% of the gas they burn while executing 4. Implement a **wall time** by capping transactions to `300Tgas` (~`300ms` of compute time) Gas in NEAR is computed on [**gas units**](/protocol/gas#gas-units) and charged using $NEAR tokens based on the network's [**gas price**](/protocol/gas#gas-price). :::tip Did you know? In NEAR, attaching extra gas to a transaction does **not** make it faster. Actions cost a fixed amount of gas, and any extra gas attached is simply sent back to the user ::: --- ## Understanding Gas Fees For every transaction, users get charged a small $NEAR fee which has to be paid **upfront**. This fee is calculated using deterministic **gas units**, and transformed into a cost in $NEAR using the network's **gas price**.
### Gas Units Every action in NEAR costs a fixed amount of **gas units**, meaning that the same operation will always cost the **same amount of gas units**. Gas units were engineered in such a way that they can be translated into compute resources, where `1Tgas` gets you approx. `1ms` of compute time. Transactions can use a maximum of `300Tgas`, meaning they should be processed in less than `300ms`, allowing the network to produce a new block approximately **every second**. :::tip Gas units encapsulate not only compute/CPU time but also bandwidth/network time and storage/IO time :::
### Gas Price To determine the actual $NEAR fee, the cost of all actions in the transaction is multiplied by a **gas price**. The gas price is **recalculated each block** based on the network's demand and floor at `1Tgas = 0.0001Ⓝ`. If the previous block is **more than half full** the price goes up by 1%, otherwise it goes down by 1% (until it reaches the floor).
What is the gas price now? You can query how much a gas unit costs in `yoctoNEAR` (1Ⓝ = `1e24` yocto) through the [`RPC`](/api/rpc/gas#gas-price). To convert in `Tgas` per `NEAR`, you can use the following formula: `gas_price * 1e12 / 1e24`. Right now, 1 Tgas costs:

### Cost for Common Actions Knowing that actions have a fixed cost in gas units, we can calculate the cost of common operations at the minimum gas price of `1Tgas = 0.0001Ⓝ`. | Action | TGas | Fee (Ⓝ) | |------------------------------|----------------|----------| | Create Account | 0.42 | 0.000042 | | Transfer NEAR | 0.45 | 0.000045 | | Add Full Access Key | 0.42 | 0.000042 | | Delete Key | 0.41 | 0.000041 | | Function Call* | ≤ 300 | ≤ 0.03 | | Deploying a `16`kb contract | 2.65 | 0.000265 | | Deploying a `X`kb contract** | 0.58 + 0.13`X` | | _Note that the fee is in $NEAR, to obtain the cost in dollars multiply by the current price of $NEAR_ :::tip Function Calls* The cost of calling a function will depend on how complex the function is, but will be consistent across function calls. Learn more below. ::: :::tip Deploying a Contract** Note that this covers the gas cost of uploading and writing bytes to storage, but does **not** cover the cost of holding them in storage (which is `1Ⓝ ~ 100kb`). :::
Where do these numbers come from? NEAR is [configured](https://github.com/near/nearcore/blob/master/core/primitives/res/runtime_configs/parameters.yaml) with base costs. An example: ```json transfer_cost: { send_sir: 115123062500, send_not_sir: 115123062500, execution: 115123062500 }, deploy_contract_cost: 184765750000, deploy_contract_cost_per_byte: 64572944 ``` The "sir" here stands for "sender is receiver". Yes, these are all identical, but that could change in the future. When you make a request to transfer funds, NEAR immediately deducts the appropriate `send` amount from your account. Then it creates a [_receipt_, an internal book-keeping mechanism](./transaction-execution.md). Creating a receipt has its own associated costs: ```json action_receipt_creation_config: { send_sir: 108059500000, send_not_sir: 108059500000, execution: 108059500000 } ``` You can query this value by using the [`protocol_config`](/api/rpc/protocol#protocol-config) RPC endpoint and search for `action_receipt_creation_config`. The appropriate amount for creating this receipt is also immediately deducted from your account. The "transfer" action won't be finalized until the next block. At this point, the `execution` amount for each of these actions will be deducted from your account. Although gas prices can change between the time of purchase and time of execution, the gas price per transaction is fixed since protocol version 78. ``` ( transfer_cost.send_not_sir + action_receipt_creation_config.send_not_sir + transfer_cost.execution + action_receipt_creation_config.execution ) * gas_price ``` Prior to protocol version 78, the gas prices of each block was used for the work done at that height. ``` ( transfer_cost.send_not_sir + action_receipt_creation_config.send_not_sir ) * gas_price_at_block_1 + ( transfer_cost.execution + action_receipt_creation_config.execution ) * gas_price_at_block_2 ```
--- ## How Do I Buy Gas? You don't buy gas, instead, the gas fee is automatically removed from your account's balance when the transaction [is first processed](./transaction-execution.md#block-1-the-transaction-arrives) based on the action's gas cost and the network's gas price. The only exception to this rule is when you make a function call to a contract. In this case, you need to define how many gas units to use, up to a maximum value of `300Tgas`. This amount will be converted to $NEAR using the network's gas price and deducted from your account's balance. If the transaction ends up using less gas than the amount deducted, the difference will simply be **refunded to your account**. However, the network subtracts a **gas refund fee** of at leat 1 Tgas or up to 5% of the unspent gas.
Gas Refund Fee Since protocol version 78, the unspent gas at the end of receipt execution is subject to a gas refund fee. The fee is still at 0 while we give projects time to adapt. The plan is to move to a fee calculated as `max(1 Tgas, 0.05 * unspent_gas) * gas_price`. The gas price is from the time of purchase. _But why introducing such a fee instead of refunding all gas?_ The reason is that attaching too much gas to function calls makes the network less efficient. Congestion control between shards gets tricky when transactions have much more gas attached than they actually use. The network limits how many cross contract calls can target a single shard per chunk, to avoid huge queues of incoming receipts on a shard. This limit sees the attached gas as an upper boundary for how much work the receipt causes on the receiving shard. Attaching too much gas can cause this limit to become too restrictive, which stalls shards unnecessarily. The other inefficiency comes from the refund receipts that prior to version 78 have been created for essentially every function call. While each of them is relatively cheap to execute, in the sum they were a significant part of the global traffic on NEAR Protocol. To read more about the deicsion to introduce this fee, please take a look at the [NEP-536](https://github.com/near/NEPs/pull/536)
:::tip In other chains, paying a higher gas price gets your transaction processed faster. In NEAR, **gas costs are deterministic**, and you **can't pay to get priority**. Any extra gas attached to a transaction is refunded, minus some fee for attaching unnecessary gas. ::: --- ## Gas as a Developer Incentive In NEAR, 30% of the gas fees burned while executing a contract go to the contract's account. This is a powerful incentive for developers to create and maintain useful contracts. For example, in [this transaction](https://testnet.nearblocks.io/txns/JD8Bg4u8kaYeaSsGBqkvhSDCEPgXhtwJRBBPKicCEPMs) the user calls a function in the `guestbook.near-examples.testnet` contract. Executing the function call burned a total of ~0.00032Ⓝ, from which 30% went to the contract's account. This means that the contract's account received 0.000096Ⓝ. Notice that the fee comes from the gas burned during the function execution, and not from the total gas used. --- ## Estimating Costs for a Call If you're developing a smart contract, you might want to estimate how much gas a function call will consume. This is useful to estimate limits for your function and avoid running into out-of-gas errors. One of the most accurate ways to estimate gas costs is by running your function in `testnet`. To know exactly how much gas a specific part of your function uses, you can use the `used_gas` method from our SDK. Another option is to use `Sandbox Testing` (available in [Rust](https://github.com/near/workspaces-rs/tree/main/examples/src) and [JavaScript](https://github.com/near/workspaces-js)), which simulates the NEAR network. There you can access the gas burnt after each function call. --- # Source: https://docs.near.org/chain-abstraction/chain-signatures/getting-started.md --- id: getting-started title: Getting Started with Chain Signatures description: "Learn how to sign and execute cross-chain transactions" --- Chain Signatures allows all NEAR accounts - including smart contracts - to sign transactions for other blockchains (such as Bitcoin, Ethereum or Solana). ![img](https://pages.near.org/wp-content/uploads/2024/02/acct-abstraction-blog-1.png) This innovation leverages Multi-Party Computation (MPC) and a distributed network of node operators to create joint signatures from arbitrary payloads, allowing NEAR accounts to control external blockchain accounts. Derive addresses on any blockchain, build DeFi on Bitcoin, create multi-chain apps with one contract, or enable account abstraction across all chains - all without the need for traditional bridges or wrapped tokens ### Chain Signatures vs. Traditional Bridges | Feature | Chain Signatures | Traditional Bridges | |----------------------------|--------------------------------------|---------------------------------| | **Security Model** | Decentralized MPC nodes | Trusted validators or relayers | | **Asset Type** | Native tokens (real BTC, ETH) | Wrapped tokens (wBTC, wETH) | | **Smart Contract Support** | Yes, contracts can sign transactions | Usually user-only | | **Chains Supported** | Any blockchain | Specific pairs only | | **Bridge Risk** | No locked funds, no bridge exploits | Billions locked, frequent hacks | | **UX** | Seamless, single account | Multiple wallets needed | --- ## How does it work? When a NEAR account - could be a user or a **smart contract** - wants to interact with a foreign blockchain, it just need to follow four simple steps. ![Chain Signatures](/assets/docs/chain-abstraction/chain-abstract-2.svg) _Chain signatures flow_ #### 1. Deriving Foreign Addresses Chain Signatures uses [derivation paths](../chain-signatures.md#derivation-paths-one-account-multiple-chains) to represent accounts on foreign blockchains The NEAR account’s name and the derivation path are used to mathematically derive a unique address for the user on the foreign blockchain
Derivation Paths A NEAR account will always derive the same address on the foreign blockchain using the same derivation path Notice that, since the foreign address is derived from the NEAR account name, it is not possible for another NEAR account to control the same address
#### 2. Creating the Transaction The client constructs the foreign transaction to be signed, which varies depending on the target blockchain :::tip You can use the [Omni Transaction](https://github.com/near/omni-transaction-rs) Rust library to build transactions easily for different blockchains (like Bitcoin and Ethereum) inside NEAR contracts and Rust clients. ::: #### 3. Requesting the Signature A NEAR account or contract calls the sign method of the MPC smart contract ([v1.signer](https://nearblocks.io/address/v1.signer)) to sign the transaction and waits while our MPC service generates the signature #### 4. Relaying the Signature Once the signature is ready, the client reconstructs the signed transaction using the signature and broadcasts it to the destination blockchain :::tip Using Chain Signatures, developers can build cross-chain DeFi applications with seamless user experiences, eliminating the need for traditional bridges. This process eliminates the need for traditional bridges and enables developers to build innovative cross-chain DeFi applications with seamless user experiences. ::: --- ## Use Cases Chain Signatures can be used to build a wide range of applications that leverage blockchain interoperability. Here are some examples: 1. **DeFi on Bitcoin (and other chain without smart contracts)** * Chain signatures allow NEAR smart contract to program assets on Bitcoin * Build lending, swaps, runes launchpads, passkey-based Bitcoin wallets, and more 2. **Chain agnostic applications** * Since chain signatures can sign transactions for all blockchains, developers can support every single chain with just one smart contract * Multichain DEXs, lending protocols, oracles, derivatives, and more 3. **Multichain account abstraction** * Users can control assets on all chains with just their NEAR account, and can utilize account abstraction features on any chain including passkeys, key rotation, etc * Using the multichain gas relayer, users can pay for gas fees on any chain using USDC 4. **Privacy** * Chain signatures can be used to encrypt and decrypt information in a programmatic way * This enables privacy applications, and even decrypting information based on ownership of assets/NFTs --- ## How to Get Started? 1. **Familiarize Yourself with Chain Signatures:** * Understand the [basics of Chain Signatures](/chain-abstraction/chain-signatures) and how they simplify blockchain interactions. * Review the technical [explainer](https://near.org/blog/unlocking-web3-usability-with-account-aggregation). 2. **Explore the Use Cases:** * Review [examples of use cases for Chain Signatures](https://pages.near.org/blog/unlocking-multichain-web3-with-near-chain-signatures/), such as Multichain DAO, Multichain NFT Minter, and Bitcoin Runes Airdrop. 3. **Access Resources and Documentation:** * Visit the [Chain Signatures documentation](/chain-abstraction/chain-signatures) for detailed technical information and code snippets. * Check out the [Linktree for Chain Signatures](https://linktr.ee/chainsignatures) for various resources, including demos and tutorials. 4. **Try the Demos:** * Use the [command-line-based demo](https://github.com/near-examples/chainsig-script) to derive accounts and send transactions on Bitcoin, Ethereum, Doge, and Ripple. * Check out the [web app demo](https://github.com/near-examples/near-multichain/tree/main). 5. **Engage with the Community:** * Join the [Chain Abstraction developers’ channel on Telegram](https://t.me/chain\_abstraction) to connect with other developers and get support. --- ## Common Questions ### What blockchains are supported? Bitcoin, Ethereum, Dogecoin, Ripple, Cosmos chains, and any blockchain that supports ECDSA or EdDSA signatures. More chains are continuously being added. ### Do I need to lock tokens in a bridge? No. Chain Signatures directly control native assets on the target blockchain. No wrapped tokens, no bridge risk. ### Can smart contracts use Chain Signatures? Yes! NEAR smart contracts can sign transactions for other blockchains, enabling programmable cross-chain applications. ### How secure is the MPC system? The MPC network is decentralized across multiple node operators. No single node has access to the full private key. Requires threshold consensus to sign. ### What's the difference from account abstraction? Chain Signatures *enables* account abstraction across all chains. Your NEAR account (with passkeys, 2FA, etc.) can now control assets on Bitcoin, Ethereum, and more. ### How much does it cost? You can sign transactions on NEAR for free, but the external account will still need tokens to pay for gas fees in the target blockchain. --- ## Where to Learn More? To dive deeper into Chain Signatures and its applications, you can explore the following resources: - **Technical Blogs and Deep Dives:** Read [high-level use cases](https://pages.near.org/blog/unlocking-multichain-web3-with-near-chain-signatures) and technical [explainer](https://near.org/blog/unlocking-web3-usability-with-account-aggregation) posts on the NEAR blog * **Community and Support:** Engage with the NEAR community on social media platforms like Twitter and participate in discussions to stay updated on the latest developments --- # Source: https://docs.near.org/tutorials/examples/global-contracts.md # Source: https://docs.near.org/smart-contracts/global-contracts.md --- id: global-contracts title: Global Contracts description: "Deploy a contract once and reuse it across accounts." --- Global contracts allow smart contracts to be deployed once and reused by any account without incurring high storage costs. Rather than deploying duplicate contracts or routing messages inefficiently across shards, NEAR developers can now think modularly and universally: write once, use everywhere. ## Overview If you've ever deployed the same contract code to multiple accounts, you’ve likely noticed that each deployment requires you to pay the full storage cost again — since the size of the WASM file determines how much `NEAR` is locked on the account. With [NEP-0591](https://github.com/near/NEPs/blob/master/neps/nep-0591.md), which introduces Global Contracts, NEAR provides a highly strategic alternative that [solves this elegantly](#solution). ## Key Features These are the features that make Global Contracts special: - **Global Addressing**: These contracts are not tied to a specific account but instead use a unique global identifier. This enables any contract, user, or application on NEAR to call the contract from any shard instantly. - **Immutable Logic**: The contract code is fixed once deployed, making it a trusted point of reference. This ensures consistency and security—ideal for system-critical protocols. - **Shared Infrastructure**: Global contracts can act as canonical libraries, utility hubs, or standards for other contracts to rely on, simplifying development and reducing duplication. - **Cross-Shard Superpowers**: Developers can build truly modular apps where parts of their stack reside on different shards but communicate via shared global logic with minimal latency or duplication. ### Use Cases - **Standard Libraries**: Reusable components for math, string operations, or token interfaces. - **DeFi Protocols**: Global contracts can anchor DEXs, lending markets, oracles—shared across all applications. - **DAO Frameworks**: Shared governance modules that any DAO can plug into, ensuring consistency and reliability. - **Identity & Credentials**: One global contract can manage decentralized identity verification and access management for the entire chain. - **Multi-part dApps**: Complex applications can split responsibilities across shards while accessing a common logic core. ## Solution Global Contracts solve the inefficiency of duplicate deployments by allowing the same contract code to be shared across multiple accounts, so storage cost is paid only once. There are two ways to reference a global contract: - **[By Account](#reference-by-account)**: an upgradable contract is published globally under a specific account ID. - **[By Hash](#reference-by-hash)**: an immutable contract is deployed globally and identified by its code hash. :::info - The contract code is distributed across all shards in the Near Protocol network, not stored inside any specific account’s storage. - The account is charged 10x more for deploying a Global Contract, at the rate 10 NEAR per 100KB. - This amount is entirely burnt and cannot be recovered later, unlike regular deployments where Near is simply locked. - The total fee is typically under 0.001 NEAR for a user to use a Global Contract, since only a few bytes are needed for the reference that is stored in the account's storage. ::: ### Reference by Account When using a reference **by account**, the contract code is tied to another account. If that account later deploys a new version of the contract, your account will automatically start using the updated code, with no need for redeployment. ### Reference by Hash When using a reference **by hash**, you reference the global contract by its immutable code hash. This ensures you're always using the exact same version, and it will never change unless you explicitly redeploy with a different hash. ## When to use Global Contracts Ask yourself the following questions before deciding how to deploy your contracts: - **_Are you working in a local environment?_** If you're just testing or building a prototype, [regular deployments](release/deploy.md) are simpler and more flexible. There's no need to burn tokens or register global references — just deploy and iterate. - **_Is the contract supposed to be deployed on many accounts?_** If the same contract will be reused across many independent accounts — say, 10 or more — Global Contracts can significantly reduce overall cost and complexity. But if only a few accounts are involved, regular deployment remains the more economical choice. - **_Are these accounts managed by your team?_** If all target accounts are under your infrastructure, you may prefer regular deployments for flexibility and cost recovery. - **_Are there more than 10 accounts?_** Global Contracts become financially efficient when reused at scale. If you're deploying the same contract to more than 10 accounts, it's likely worth considering. - **_Do you need to upgrade the contract across many accounts in one step, even if it requires burning tokens?_** If you want to be able to push updates to all deployed instances at once, then go with Global Contracts by Account ID, but keep in mind that the deployment cost is non-refundable. - **_Does your use case require the contract to be permanently immutable?_** If the contract must never change, for example, due to security, compliance, or user trust, then using a Global Contract by Code Hash ensures immutability at the protocol level. ## Deploying a Global Contract Global contracts can be deployed in 2 ways: either by their [hash](#reference-by-hash) or by the owner [account ID](#reference-by-account). Contracts deployed by hash are effectively immutable and cannot be updated. When deployed by account ID the owner can redeploy the contract updating it for all its users. Global contracts can be deployed using [`NEAR CLI`](../tutorials/examples/global-contracts.md#deployment) or by code using [NEAR APIs](../tools/near-api.md#deploy-a-global-contract). Check [this tutorial](../tutorials/examples/global-contracts.md) to learn how to deploy and use Global Contracts using CLI, JavaScript, or Rust. :::info Deploying a global contract incurs high storage costs. Tokens are burned to compensate for storing the contract on-chain, unlike regular contracts where tokens are locked based on contract size. ::: ### About Contract updates If a contract is expected to get upgrades from time to time, then it should be rolled out using the `Account Id` strategy. Using a Global Contract by `Hash` is for immutable contracts that are supposed to stay the same forever. Keep in mind, when using a Global Contract by [account ID](#reference-by-account), the one that handles the contract update is the original account, so they're responsible for contract state upgrading too. --- # Source: https://docs.near.org/tutorials/examples/guest-book.md --- 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" --- 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. ``` import '@/styles/globals.css'; import '@near-wallet-selector/modal-ui/styles.css'; import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; import { setupMeteorWallet } from '@near-wallet-selector/meteor-wallet'; import { setupMeteorWalletApp } from '@near-wallet-selector/meteor-wallet-app'; import { setupBitteWallet } from '@near-wallet-selector/bitte-wallet'; import { setupEthereumWallets } from '@near-wallet-selector/ethereum-wallets'; import { setupHotWallet } from '@near-wallet-selector/hot-wallet'; import { setupLedger } from '@near-wallet-selector/ledger'; import { setupSender } from '@near-wallet-selector/sender'; import { setupHereWallet } from '@near-wallet-selector/here-wallet'; import { setupNearMobileWallet } from '@near-wallet-selector/near-mobile-wallet'; import { setupWelldoneWallet } from '@near-wallet-selector/welldone-wallet'; import { GuestbookNearContract, NetworkId } from '@/config'; import { WalletSelectorProvider } from '@near-wallet-selector/react-hook'; import { Navigation } from '@/components/Navigation'; import { wagmiAdapter, web3Modal } from '@/wallets/web3modal'; const walletSelectorConfig = { network: NetworkId, createAccessKeyFor: GuestbookNearContract, modules: [ setupEthereumWallets({ wagmiConfig: wagmiAdapter.wagmiConfig, web3Modal }), setupBitteWallet(), setupMeteorWallet(), setupMeteorWalletApp({contractId: GuestbookNearContract}), setupHotWallet(), setupLedger(), setupSender(), setupHereWallet(), setupNearMobileWallet(), setupWelldoneWallet(), setupMyNearWallet(), ], } export default function App({ Component, pageProps }) { return ( ); } ``` ``` import { utils } from "near-api-js"; import { useState, useEffect } from "react"; import Form from "@/components/Form"; import SignIn from "@/components/SignIn"; import Messages from "@/components/Messages"; import styles from "@/styles/app.module.css"; import { GuestbookNearContract } from "@/config"; import { useWalletSelector } from '@near-wallet-selector/react-hook'; export default function Home() { const { signedAccountId, viewFunction, callFunction } = useWalletSelector(); const [messages, setMessages] = useState([]); useEffect(() => { getLast10Messages().then((messages) => setMessages(messages.reverse())); }, []); const getLast10Messages = async () => { const total_messages = await viewFunction({ contractId: GuestbookNearContract, method: "total_messages", }); const from_index = total_messages >= 10 ? total_messages - 10 : 0; return viewFunction({ contractId: GuestbookNearContract, method: "get_messages", args: { from_index: String(from_index), limit: "10" }, }); }; const onSubmit = async (e) => { e.preventDefault(); const { fieldset, message, donation } = e.target.elements; fieldset.disabled = true; // Add message to the guest book const deposit = utils.format.parseNearAmount(donation.value); callFunction({ contractId: GuestbookNearContract, method: "add_message", args: { text: message.value }, deposit, }).catch(() => { // rollback to the current messages setMessages(messages); }); await new Promise(resolve => setTimeout(resolve, 300)); fieldset.disabled = false; setMessages([{ sender: signedAccountId, text: message.value, premium: donation.value >= 1 }, ...messages]); }; return (

📖 NEAR Guest Book

{signedAccountId ? (
) : ( )}
{!!messages.length && }
); } ```
--- ## Smart Contract The contract presents 3 methods: `add_message`, `get_message` and `total_messages`. ``` @NearBindgen({}) class GuestBook { messages: Vector = new Vector("v-uid"); static schema = { 'messages': { class: Vector, value: PostedMessage } } @call({ payableFunction: true }) // Public - Adds a new message. add_message({ text }: { text: string }) { // If the user attaches more than 0.1N the message is premium const premium = near.attachedDeposit() >= BigInt(POINT_ONE); const sender = near.predecessorAccountId(); const message = new PostedMessage(premium, sender, text); this.messages.push(message); } @view({}) // Returns an array of messages. get_messages({ from_index = 0, limit = 10 }: { from_index: number, limit: number }): PostedMessage[] { return this.messages.toArray().slice(from_index, from_index + limit); } ``` ``` #[near] impl Contract { // Public Method - Adds a message to the vector #[payable] pub fn add_message(&mut self, text: String) { // If the user attaches more than 0.01N the message is premium let premium = env::attached_deposit() >= POINT_ONE; let sender = env::predecessor_account_id(); let message = PostedMessage { premium, sender, text, }; self.messages.push(message); } // Public Method - Returns a slice of the messages. pub fn get_messages(&self, from_index: Option, limit: Option) -> Vec<&PostedMessage> { let from = u64::from(from_index.unwrap_or(U64(0))); let limit = u64::from(limit.unwrap_or(U64(10))); self.messages .iter() .skip(from as usize) .take(limit as usize) .collect() } pub fn total_messages(&self) -> u32 { self.messages.len() } } ```
### 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` ::: --- # Source: https://docs.near.org/chain-abstraction/omnibridge/how-it-works.md --- id: how-it-works sidebar_label: How It Works title: How Omni Bridge Works description: "Learn how Omni Bridge uses Chain Signatures to enable cross-chain transfers." --- The journey toward truly trustless cross-chain communication took a significant leap forward when the NEAR team [created the first trustless bridge with Ethereum](https://near.org/blog/the-rainbow-bridge-is-live) (Rainbow Bridge). This pioneering achievement demonstrated that completely trustless cross-chain communication was possible, marking a crucial step toward the vision of chain abstraction. However, this approach relied on implementing a NEAR light client directly on Ethereum - essentially requiring Ethereum to understand and verify NEAR's complex blockchain rules. Omni Bridge introduces a more elegant solution using Chain Signatures. Instead of running light clients on each destination chain, it leverages Chain Signature's MPC Service to enable secure cross-chain message verification without the overhead of light client verification. This new approach reduces verification times from hours to minutes while significantly reducing gas costs across all supported chains. ### Issues with Light Clients A light client is a smart contract that lets one blockchain verify events happening on another blockchain. In Rainbow Bridge's case, the Ethereum light client needs to track NEAR's blocks, verify its validators' signatures, and confirm transactions. This comes with major technical challenges: it requires storing two weeks of Ethereum block data, maintaining an updated list of NEAR validators and their stakes, and most crucially, verifying NEAR's ED25519 signatures - a process Ethereum wasn't built for. This verification is computationally expensive, making the whole process slow, costly, and ultimately a major bottleneck. For example, with Rainbow Bridge, transactions from NEAR to Ethereum take between 4 and 8 hours due to the 4-hour challenge period and block submission intervals driven by Ethereum's high gas costs. More importantly, this approach becomes increasingly impractical when connecting to multiple chains, as each chain would require its own light client implementation. Some chains, such as Bitcoin, don't even support smart contracts, making it technically impossible to implement a NEAR light client. While we still need to support light clients of different networks on NEAR (which is significantly easier to implement), a different approach is needed for verifying NEAR state on foreign chains. ### Token Standards and Cross-Chain Communication Before exploring how Chain Signatures solves these issues, it's important to understand how tokens work on NEAR. [NEP-141](https://github.com/near/NEPs/tree/master/neps/nep-0141.md), NEAR's fungible token standard, has a key feature that sets it apart from Ethereum's ERC-20: built-in composability through transfer-and-call functionality. When a token transfer happens on NEAR using `ft_transfer_call`, the token contract first transfers the tokens and then automatically calls the specified `ft_on_transfer` method on the receiver contract. While these operations happen in sequence within the same transaction, the receiver contract has the ability to reject the transfer, causing the tokens to be refunded. This atomic behavior ensures the integrity and safety of bridge operations by preventing partial execution states. For more information see [Fungible Tokens](../../primitives/ft/ft.md). ## Enter Chain Signatures Instead of maintaining complex light clients on destination chains, Chain Signatures introduces a fundamentally different approach based on three core components: 1. **Deterministic Address Derivation** - Every NEAR account can mathematically derive addresses on other chains through derivation paths. This isn't just a mapping - it's a cryptographic derivation that ensures the same NEAR account always controls the same set of addresses across all supported chains. 2. **Bridge Smart Contract** - A central contract on NEAR coordinates with the MPC network to generate secure signatures for cross-chain transactions. This contract handles the token locking and requesting of signatures for outbound transfers 3. **MPC Service** - A decentralized network of nodes that jointly sign transactions without ever reconstructing a full private key. The security comes from threshold cryptography - no single node or small group of nodes can create valid signatures alone. ## Putting It All Together As we've learned, Chain Signatures fundamentally changes the verification mechanism for cross-chain messages. Here's what this means in practice: The light client approach requires destination chains to verify ED25519 signatures from NEAR validators. Chain Signatures replaces this with a single MPC signature verification. Destination chains only need to verify one signature using their native signature verification schemes - typically ECDSA for EVM chains. NEP-141's transaction guarantees handle the security of token locking. A transfer creates two operations within a **single transaction**: 1. Lock tokens and record the transfer state 2. Request MPC signature for the destination chain The Locker contract requests signatures from the MPC network, which then generates signatures for valid transfer requests. This replaces the need for challenge periods - the security derives from the MPC threshold guarantees rather than optimistic assumptions. Adding new chains becomes a matter of implementing three standard components: 1. Chain-specific address derivation 2. MPC signature verification (or transaction signing for chains like Bitcoin) 3. Bridge contract deployment 4. Communication path for transfers back to NEAR (currently using Wormhole for newer chains) While we still need light clients on NEAR for receiving transfers from other chains, this approach makes it feasible to support a wider range of chains without implementing complex verification logic on each destination chain. ```mermaid sequenceDiagram title: High-Level Overview of NEAR to External Chain Transfer participant User as User Account participant Bridge as Omni Bridge
Locker Contract participant MPC as MPC Service
(off-chain) participant Other as Destination Chain note over User, Bridge: NEAR Blockchain User->>Bridge:1. Submits transfer
token request Bridge->>Bridge: 2. Locks tokens Bridge->>MPC: 3. Request signature MPC->>MPC: 3. Signs message MPC-->>Bridge: 4. Return signed msg Bridge->>Other: 5. Broadcast signed msg to destination chain Other->>Other: 4. Mint/release tokens ``` To get started building with Omni Bridge, see: - [Bridge SDK JS](https://github.com/near-one/bridge-sdk-js) Omni Bridge implementation in JavaScript - [Bridge SDK Rust](https://github.com/near-one/bridge-sdk-rs) Omni Bridge implementation in Rust --- # Source: https://docs.near.org/chain-abstraction/omnibridge/implementation-details.md --- id: implementation-details sidebar_label: Implementation Details title: Implementation Details description: "Explore Omni Bridge's technical architecture" --- The Omni Bridge is a sophisticated cross-chain bridge infrastructure that enables secure and efficient token transfers between NEAR Protocol and various other blockchain networks. This document provides a detailed technical overview of the bridge's architecture, covering its core components, security model, and operational mechanisms. By leveraging a combination of Multi-Party Computation (MPC), chain-specific light clients, and a permissionless relayer network, the bridge achieves a robust balance of security, decentralization, and user experience. For reference code implementations, see: - [Bridge SDK JS](https://github.com/near-one/bridge-sdk-js) Omni Bridge implementation in JavaScript - [Bridge SDK Rust](https://github.com/near-one/bridge-sdk-rs) Omni Bridge implementation in Rust --- ## The Bridge Token Factory Pattern At the core of Omni Bridge is the Bridge Token Factory contract on NEAR that serves as both a token factory and custodian. This unified contract handles both native tokens from the source chain and bridged tokens created by the factory itself. This design simplifies maintenance and reduces complexity compared to having separate contracts. The contract has several key responsibilities: ### For bridged tokens (tokens originally from other chains): * Deploys new token contracts when bridging tokens for the first time * Mints tokens when receiving valid transfer messages * Burns tokens when initiating transfers back to the origin chain ### For native NEAR tokens: * Acts as a custodian by locking tokens during transfers * Releases tokens when receiving valid transfer messages * Manages token operations through the NEP-141 standard ### Transfer Lifecycle A transfer's lifecycle includes several states, shown below for a NEAR to Ethereum transfer of native NEAR tokens: ```mermaid stateDiagram-v2 [*] --> Initiated: User calls transfer Initiated --> Locked: Tokens locked in bridge Locked --> Signed: MPC signature generated Signed --> Completed: Tokens released on Ethereum Completed --> [*] ``` --- ## Message Signing and Verification For most chains, the bridge uses a payload-based message signing system (with Bitcoin being a notable exception requiring full transaction signing). ### Message Types The bridge supports several types of signed messages: * **Transfer Messages** * Initiation messages * Finalization messages * **Token Messages** * Deployment messages * Metadata update messages ### Payload Structure Messages are encoded using Borsh serialization and contain: | Component | Description | |-----------|-------------| | Message Type | Identifier for the message category | | Chain Info | Chain IDs and relevant addresses | | Operation Data | Amounts, recipients, fees, etc. | ### Signature Process 1. NEAR contract creates and stores the message payload 2. MPC network observers detect valid payloads 3. Nodes jointly sign the payload 4. Signature is verified on destination chains :::tip Key Benefits * Clearer message intent through structured payloads * More efficient signature verification on destination chains * Standardized message format across chains ::: ## Transaction Flow: NEAR to Other Chains Here's an overview of how transfers are processed from NEAR to different destination chains: ```mermaid flowchart TD Start[User Initiates Transfer] --> TokenCheck{Token Type?} TokenCheck -->|NEAR Native| Lock[Lock in Bridge Contract] TokenCheck -->|Bridged Token| Burn[Burn Token] Lock --> MPCSign[Request MPC Signature] Burn --> MPCSign MPCSign --> Chain{Destination Chain} Chain -->|Bitcoin| BTCBridge[Bitcoin Script] Chain -->|Other| OtherBridge[Verify MPC Signature] BTCBridge --> Mint[Mint/Release Tokens] OtherBridge --> Mint Mint --> End[Transfer Complete] ``` ### Transfer Process Let's follow what happens when a user wants to transfer tokens from NEAR to another chain: #### 1. Initiation The user starts by calling the token contract with: * Amount to transfer * Destination chain and address * Fee preferences (whether to pay fees in the token being transferred or in NEAR) * Fees are minted on NEAR side for relayers #### 2. Token Lock The token contract transfers tokens to the locker contract, which: * Validates the transfer message * Assigns a unique nonce * Records the pending transfer * Emits a transfer event #### 3. MPC Signing The bridge contract: * Requests signature generation * MPC nodes jointly generate and aggregate signature * Maintains threshold security throughout process #### 4. Destination Chain The Bridge Token Factory on the destination chain: * Verifies the MPC signature * Mints equivalent tokens --- ## Transaction Flow: Other Chains to NEAR The reverse flow varies based on the source chain: ### 1. Ethereum Uses NEAR light client for maximum security: * Burning tokens on source chain * Submitting proof to NEAR * Verifying proof through light client * Releasing tokens to recipient ### 2. Supported Non-EVM Chains (e.g., Solana) Utilize established message passing protocols (such as Wormhole) for: * Message passing between chains * Transaction verification * Integration with NEAR token factory system ### 3. Other EVM Chains Utilize a combination of light clients (where efficient) and message passing protocols to ensure secure verification of inbound transfers. --- ## Transaction Flow: Chain to Chain (via NEAR) For transfers between two non-NEAR chains (e.g., Ethereum to Solana), the bridge combines both flows using NEAR as an intermediary routing layer. Rather than minting or unlocking tokens on NEAR, the bridge creates a forwarding message that directs tokens to be minted or unlocked on the final destination chain. From the user's perspective, this appears as a single operation - they initiate the transfer on the source chain, and the off-chain relayer infrastructure handles the intermediate NEAR routing automatically. --- ## Security Model ### Trust Assumptions Omni Bridge requires different trust assumptions depending on the chain connection: #### For Chain Signatures: * NEAR Protocol security (2/3+ honest validators) * MPC network security (2/3+ honest nodes) * No single entity controls enough MPC nodes to forge signatures * Correct implementation of the signing protocol #### For Ethereum/Bitcoin connections: * Light client security * Finality assumptions (e.g., sufficient block confirmations) * Chain-specific consensus assumptions #### For Message Passing connections: * Security of the underlying message passing protocol (e.g., Wormhole Guardian network) * Verified by NEAR network participants (e.g., validators and full nodes) --- ## Relayer Network Relayers are permissionless infrastructure operators who monitor for bridge events and execute cross-chain transactions. Unlike many bridge designs, our relayers cannot: * Forge transfers * Steal funds * Censor transactions (users can self-relay) * Front-run transactions for profit * Do not create additional security assumptions :::info The relayer's role is purely operational - executing valid transfers and collecting predetermined fees. Multiple relayers can operate simultaneously, creating competition for faster execution and lower fees. ::: --- ## Fast Transfers Standard cross-chain transfers can take time due to finality and verification requirements. **Fast Transfers** allow relayers to expedite this process by fronting liquidity. ### How it Works 1. **User Initiation:** A user sends a `FastFinTransferMsg` specifying the destination and fee. 2. **Relayer Execution:** A relayer detects the request and instantly transfers the equivalent amount (minus fees) to the user on the destination chain from their own funds. 3. **Settlement:** The bridge later reimburses the relayer once the original transfer is fully verified and finalized. :::tip Fast transfers are ideal for users who prioritize speed over cost, as relayers may charge a premium for the liquidity and convenience. ::: --- ## Multi-Token Support (ERC1155) Omni Bridge supports the **ERC1155** standard, enabling the transfer of multiple token types within a single contract. ### Address Derivation To maintain consistency across chains, bridged ERC1155 tokens use a deterministic address derivation scheme: * **Deterministic Address:** `keccak256(tokenAddress + tokenId)` * This ensures that each `tokenId` within an ERC1155 contract maps to a unique, consistent address on the destination chain. ### Key Functions * **`initTransfer1155`**: Initiates a transfer for a specific ERC1155 token ID. * **`logMetadata1155`**: Registers metadata for a specific token ID, ensuring it is recognized by indexers and wallets. --- ## Fee Structure Bridge fees are unified and processed on NEAR, with components including: ### Execution Fees * Destination chain gas costs * Source chain storage costs * Relayer operational costs * MPC signing costs ### Fee Payment Options * Native tokens of source chain * The token being transferred :::note Fees dynamically adjust based on gas prices across different chains to ensure reliable execution. ::: ### Design Goals The fee structure is designed to: * Ensure relayer economic viability * Prevent economic attacks * Allow fee market competition * Cover worst-case execution costs :::tip Users can bypass relayers entirely by executing their own transfers, paying only the necessary gas fees on each chain. This creates a natural ceiling on relayer fees. ::: --- # Source: https://docs.near.org/chain-abstraction/chain-signatures/implementation.md --- id: implementation title: Implementing Chain Signatures description: "Learn how to sign transactions across multiple blockchains." --- Chain signatures enable NEAR accounts, including smart contracts, to sign and execute transactions across many blockchain protocols. This unlocks the next level of blockchain interoperability by giving ownership of diverse assets, cross-chain accounts, and data to a single NEAR account.
Supported Networks While you can sign transactions for any network using Eddsa or Ecdsa keys, each chain signs transactions differently. Our example implementation shows you how to sign transactions for: Bitcoin, Solana, Cosmos, XRP, Aptos, Sui and EVM networks (Ethereum, Base, BNB Chain, Avalanche, Polygon, Arbitrum, and more).
--- ## Create a Chain Signature There are five steps to create a Chain Signature: 1. [Deriving the Foreign Address](#1-deriving-the-foreign-address) - Derive the address that will be controlled on the target blockchain. 2. [Creating a Transaction](#2-creating-the-transaction) - Create the transaction or message to be signed. 3. [Requesting a Signature](#3-requesting-the-signature) - Call the NEAR MPC contract requesting it to sign the transaction. 4. [Formatting the Signature](#4-formatting-the-signature) - Format the signature from the MPC contract and add it to the transaction. 5. [Relaying the Signed Transaction](#5-relaying-the-signed-transaction) - Send the signed transaction to the destination chain for execution. ![chain-signatures](/assets/docs/welcome-pages/chain-signatures-overview.png) _Diagram of a chain signature in NEAR_ The [chainsig.js](https://github.com/NearDeFi/chainsig.js) library provides a convenient interface for completing each of these steps. :::tip For building transactions inside of NEAR smart contracts written in Rust, you can use the [Omni Transaction](https://github.com/near/omni-transaction-rs) library to easily build transactions for different blockchains (like Bitcoin and Ethereum). ::: :::info MPC Contracts There is an [MPC contract](https://github.com/Near-One/mpc/tree/main/libs/chain-signatures/contract) available on both `mainnet` and `testnet`: - Mainnet: `v1.signer` - Testnet: `v1.signer-prod.testnet` The MPC network is made up of 8 nodes. ::: --- ## Chain Signatures Contract To interact with the chain signatures library you first need to instantiate a `ChainSignaturesContract`. ``` export const SIGNET_CONTRACT = new contracts.ChainSignatureContract({ networkId: NetworkId, contractId: MPC_CONTRACT, }); ``` The `networkId` and `contractId` are set to the values specified in the previous section depending which network you are on. --- ## Chain Adapters To interact with a specific chain, you need to instantiate the relevant `chainAdapter`. ``` const publicClient = createPublicClient({ transport: http(rpcUrl), }); const Evm = new chainAdapters.evm.EVM({ publicClient, contract: SIGNET_CONTRACT, }); ``` The EVM chain adapter takes the `ChainSignaturesContract` as an argument as well as `publicClient` which is constructed from an EVM RPC URL. :::tip To use different EVM networks, simply specify the RPC URL for your desired network. The example demonstrates compatibility with multiple EVM-compatible networks including Ethereum, Base, BNB Chain, Avalanche, Polygon, Arbitrum, zkSync, and many others. You can find RPC URLs for various networks at [ChainList](https://chainlist.org/?testnets=true). ::: ``` const btcRpcAdapter = new chainAdapters.btc.BTCRpcAdapters.Mempool( "https://mempool.space/testnet4/api", ); const Bitcoin = new chainAdapters.btc.Bitcoin({ network: NetworkId, btcRpcAdapter, contract: SIGNET_CONTRACT, }); ``` The Bitcoin chain adapter takes the `ChainSignaturesContract` as an argument as well as the `network` ("mainnet", "testnet" or "regtest") and a `btcRpcAdapter` which handles communication with the Bitcoin network. ``` const connection = new SolanaConnection("https://api.devnet.solana.com"); const Solana = new chainAdapters.solana.Solana({ solanaConnection: connection, contract: SIGNET_CONTRACT, }); ``` The Solana chain adapter takes the `ChainSignaturesContract` as an argument as well as a `connection` which is constructed from a Solana RPC URL. If you want to use Mainnet, then you need to choose a Mainnet RPC. ``` const Xrp = new chainAdapters.xrp.XRP({ rpcUrl: "wss://s.altnet.rippletest.net:51233/", contract: SIGNET_CONTRACT, }); ``` The XRP chain adapter takes the `ChainSignaturesContract` as an argument as well as an `rpcUrl` for the XRP Ledger and the `rpcURl` specification. For testnet, use `https://s.altnet.rippletest.net:51234`. ``` const rpcUrl = getFullnodeUrl("testnet"); const suiClient = new SuiClient({ url: rpcUrl }); const Sui = new chainAdapters.sui.SUI({ client: suiClient, contract: SIGNET_CONTRACT, rpcUrl: rpcUrl, }); ``` The SUI chain adapter takes the `ChainSignaturesContract` as an argument as well as a `connection` which is a SuiClient constructed from a SUI RPC URL. ``` const aptosClient = new AptosClient( new AptosConfig({ network: Network.TESTNET, }), ); const Aptos = new chainAdapters.aptos.Aptos({ client: aptosClient, contract: SIGNET_CONTRACT, }); ``` The Aptos chain adapter takes the `ChainSignaturesContract` as an argument as well as a `nodeUrl` for the Aptos network and the `network` specification. --- ## 1. Deriving the Foreign Address Chain Signatures use [`derivation paths`](../chain-signatures.md#derivation-paths-one-account-multiple-chains) to represent accounts on the target blockchain. The foreign address to be controlled can be deterministically derived from: - The NEAR account calling the MPC contract (e.g., `example.near`, `example.testnet`, etc.) - A derivation path (a string such as `ethereum-1`, `ethereum-2`, etc.) - The MPC service's master public key (we don't need to worry about this as it is defined in the library we're using). To derive the address call the `deriveAddressAndPublicKey` method passing the near account Id from which the address is being derived and the derivation path. ``` const { address } = await Evm.deriveAddressAndPublicKey( signedAccountId, debouncedDerivationPath, ); setSenderAddress(address); ``` ``` const { address, publicKey } = await Bitcoin.deriveAddressAndPublicKey( signedAccountId, debouncedDerivationPath, ); setSenderAddress(address); ``` ``` const { publicKey } = await Solana.deriveAddressAndPublicKey( signedAccountId, debouncedDerivationPath, ); ``` On Solana, your address is the same as your public key. ``` const { address, publicKey } = await Xrp.deriveAddressAndPublicKey( signedAccountId, debouncedDerivationPath, ); ``` ``` const { address, publicKey } = await Sui.deriveAddressAndPublicKey( signedAccountId, debouncedDerivationPath, ); ``` ``` const { address, publicKey } = await Aptos.deriveAddressAndPublicKey( signedAccountId, debouncedDerivationPath, ); ``` :::info The same NEAR account and path will always produce the same address on the target blockchain. - `example.near` + `ethereum-1` = `0x1b48b83a308ea4beb845db088180dc3389f8aa3b` - `example.near` + `ethereum-2` = `0x99c5d3025dc736541f2d97c3ef3c90de4d221315` ::: --- ## 2. Creating the Transaction To construct the transaction to be signed use the method `prepareTransactionForSigning`. Constructing a transaction to transfer ETH is very simple. The `value` is the amount of ETH in Wei as type BigInt (1 ETH = 1018 Wei). ``` return await Evm.prepareTransactionForSigning({ from: senderAddress, to: receiverAddress, value: BigInt(Web3.utils.toWei(transferAmount, "ether")), }); }, ``` This method returns the `unsigned transaction` and the transaction `hash(es)` (also known as the `payload`). Constructing a transaction to transfer BTC is very simple. The `value` is the amount of BTC in satoshis as a string (1 BTC = 100,000,000 sats). ``` const { transaction, hashesToSign } = await Bitcoin.prepareTransactionForSigning({ publicKey: senderPublicKey, from: senderAddress, to: receiverAddress, value: transferAmount.toString(), }); ``` This method returns the `unsigned transaction` and the transaction `hash(es)` (also known as the `payload`). Constructing a transaction to transfer SOL is very simple. The `value` is the amount of SOL in lamports as type BigInt (1 SOL = 1,000,000,000 lamports). ``` const { transaction: { transaction }, } = await Solana.prepareTransactionForSigning({ from: senderAddress, to: receiverAddress, amount: decimalToBigInt(transferAmount, 9), }); ``` This method returns the `unsigned transaction`. Constructing a transaction to transfer XRP is straightforward. The `value` is the amount of XRP in drops as a string (1 XRP = 1,000,000 drops). ``` const { transaction, hashesToSign } = await Xrp.prepareTransactionForSigning({ from: senderAddress, to: receiverAddress, amount: decimalToBigInt(transferAmount, 6).toString(), publicKey: senderPublicKey, }); ``` This method returns the `unsigned transaction` and the transaction `hash` (also known as the `payload`). Constructing a transaction to transfer SUI is simple. The `value` is the amount of SUI in MIST as type BigInt (1 SUI = 1,000,000,000 MIST). ``` const transactionSui = new Transaction(); const [coin] = transactionSui.splitCoins(transactionSui.gas, [ decimalToBigInt(transferAmount, 9), ]); transactionSui.transferObjects([coin], receiverAddress); transactionSui.setSender(senderAddress); const { hashesToSign, transaction } = await Sui.prepareTransactionForSigning(transactionSui); ``` This method returns the `unsigned transaction` and the transaction `hash` (also known as the `payload`). Constructing a transaction to transfer APT is straightforward. The `value` is the amount of APT in octas as type BigInt (1 APT = 100,000,000 octas). ``` const transactionPayload = { function: "0x1::aptos_account::transfer", functionArguments: [receiverAddress, decimalToBigInt(transferAmount, 8)], }; const transaction = await aptosClient.transaction.build.simple({ sender: senderAddress, data: transactionPayload, }); const { hashesToSign } = await Aptos.prepareTransactionForSigning(transaction); setStatus( ``` This method returns the `unsigned transaction` and the transaction `hash` (also known as the `payload`).
EVM Function Calls To call a function on a smart contract we need the ABI of the contract, in our repo this is defined in the [config.js](https://github.com/near-examples/near-multichain/blob/main/src/config.js#L23-L63) file (this can be gathered from Remix or using Etherscan). Then define a `Contract` object using the `ethers` library ``` const contract = new Contract(contractAddress, ABI, provider); ``` Then to construct the transaction ``` const data = contract.interface.encodeFunctionData("set", [number]); return await Evm.prepareTransactionForSigning({ from: senderAddress, to: contractAddress, data, }); }, ``` This approach allows you to call smart contract functions by encoding the function data and including it in the transaction.
--- ## 3. Requesting the Signature Once the transaction is created and ready to be signed, a signature request is made by calling `sign` on the MPC smart contract. The method requires four parameters: 1. The `payloads` (or hashes) to be signed for the target blockchain 2. The derivation `path` for the account we want to use to sign the transaction 3. The `keyType`, `Ecdsa` for `Secp256k1` signatures and `Eddsa` for `Ed25519` signatures. 4. The `signerAccount` which contains the `accountId` that is signing and the `signAndSendTransactions` function from [Near Connect](../../web3-apps/tutorials/wallet-login). ``` const rsvSignatures = await SIGNET_CONTRACT.sign({ payloads: hashesToSign, path: debouncedDerivationPath, keyType: "Ecdsa", signerAccount: { accountId: signedAccountId, signAndSendTransactions, }, }); ``` ``` const rsvSignatures = await SIGNET_CONTRACT.sign({ payloads: hashesToSign, path: debouncedDerivationPath, keyType: "Ecdsa", signerAccount: { accountId: signedAccountId, signAndSendTransactions, }, }); ``` For Bitcoin, it is common to have multiple UTXOs to sign when sending a single transaction. We create a NEAR transaction (to call `sign` on the MPC contract) for each UTXO and send them to be signed by the MPC individually. Each signature is then parsed from each transaction outcome to produce an array of signatures. To get the payload, serialize the transaction to a `uint8Array` and then convert it to hex. ``` const rsvSignatures = await SIGNET_CONTRACT.sign({ payloads: [transaction.serializeMessage()], path: debouncedDerivationPath, keyType: "Eddsa", signerAccount: { accountId: signedAccountId, signAndSendTransactions, }, }); ``` ``` const rsvSignatures = await SIGNET_CONTRACT.sign({ payloads: hashesToSign, path: debouncedDerivationPath, keyType: "Ecdsa", signerAccount: { accountId: signedAccountId, signAndSendTransactions, }, }); ``` ``` const rsvSignatures = await SIGNET_CONTRACT.sign({ payloads: hashesToSign, path: debouncedDerivationPath, keyType: "Eddsa", signerAccount: { accountId: signedAccountId, signAndSendTransactions, }, }); ``` ``` const rsvSignatures = await SIGNET_CONTRACT.sign({ payloads: hashesToSign, path: debouncedDerivationPath, keyType: "Eddsa", signerAccount: { accountId: signedAccountId, signAndSendTransactions, }, }); ``` :::info The contract will take some time to respond, as the `sign` method [yields execution](/blog/yield-resume), waiting for the MPC service to sign the transaction. ::: --- ## 4. Formatting the Signature Once the signature is returned from the MPC it needs to be formatted and added to the transaction to produce a signed transaction. ``` const finalizedTransaction = Evm.finalizeTransactionSigning({ transaction, rsvSignatures, }); ``` ``` const finalizedTransaction = Bitcoin.finalizeTransactionSigning({ transaction, rsvSignatures, }); ``` For Bitcoin, the array of signatures is added to the transaction to produce a complete signed transaction. ``` const finalizedTransaction = Solana.finalizeTransactionSigning({ transaction, rsvSignatures: rsvSignatures[0], senderAddress, }); ``` ``` const finalizedTransaction = Xrp.finalizeTransactionSigning({ transaction, rsvSignatures, }); ``` ``` const finalizedTransaction = Sui.finalizeTransactionSigning({ transaction, rsvSignatures: rsvSignatures[0], publicKey: senderPublicKey, }); ``` ``` const finalizedTransaction = Aptos.finalizeTransactionSigning({ transaction, rsvSignatures: rsvSignatures[0], publicKey: senderPublicKey, }); ``` --- ## 5. Relaying the Signed Transaction Now that we have a signed transaction, we can relay it to the target network using `broadcastTx`. ``` const transactionHash = await Evm.broadcastTx(signedTransaction); setStatus( ``` ``` const transactionHash = await Bitcoin.broadcastTx(signedTransaction); ``` ``` const transactionHash = await Solana.broadcastTx(signedTransaction); ``` ``` const transactionHash = await Xrp.broadcastTx(signedTransaction); ``` ``` const transactionHash = await Sui.broadcastTx(signedTransaction); ``` ``` const transactionHash = await Aptos.broadcastTx(signedTransaction); ``` The method returns a transaction hash which can be used to locate the transaction on an explorer. :::info ⭐️ For a deep dive into the concepts of Chain Signatures, see [What are Chain Signatures?](../chain-signatures.md) ⭐️ For a complete example of a NEAR account using chain signatures in a frontend, see our [web app example](https://github.com/near-examples/near-multichain). ::: --- # Source: https://docs.near.org/integrations/implicit-accounts.md --- id: implicit-accounts title: Implicit Accounts sidebar_label: Implicit Accounts description: "Learn about implicit accounts in NEAR, how they work with Ethereum-style addresses, and their role in chain abstraction and multi-chain integration." --- This document provides an overview of implicit accounts in the NEAR Protocol, including how to create them, use them for transactions, and manage them effectively. Implicit accounts are particularly useful for exchanges and other services that need to facilitate transactions without requiring user interaction. ## Background {#background} Implicit accounts work similarly to Bitcoin/Ethereum accounts. - They allow you to reserve an account ID before it's created by generating a ED25519 key-pair locally. - This key-pair has a public key that maps to the account ID. - The account ID is a lowercase hex representation of the public key. - An ED25519 Public key contains 32 bytes that maps to 64 characters account ID. - The corresponding secret key allows you to sign transactions on behalf of this account once it's created on chain. :::info You can find the implicit accounts specification [here](https://nomicon.io/DataStructures/Account.html#implicit-account-ids). ::: ## Creating an account locally {#creating-an-account-locally} For the purpose of this demo, we'll use the `betanet` network. ## Set `betanet` network {#set-betanet-network} ```bash export NEAR_ENV=betanet ``` ## Generating the Implicit account ```bash near account create-account fund-later use-auto-generation save-to-folder ~/.near-credentials/implicit ``` Example Output ``` The file "~/.near-credentials/testnet/8bca86065be487de45e795b2c3154fe834d53ffa07e0a44f29e76a2a5f075df8.json" was saved successfully Here is your console command if you need to script it or re-run: near account create-account fund-later use-auto-generation save-to-folder ~/.near-credentials/implicit ``` ## Using the Implicit Account We can export our account ID to a bash env variable: ```bash export ACCOUNT="8bca86065be487de45e795b2c3154fe834d53ffa07e0a44f29e76a2a5f075df8" ``` Assuming you've received tokens on your new account, you can transfer from it using the following command: ```bash near tokens $ACCOUNT send-near ' NEAR' network-config testnet sign-with-keychain send ``` ## Transferring to the implicit account {#transferring-to-the-implicit-account} Let's say someone gives you their account ID `0861ea8ddd696525696ccf3148dd706c4fda981c64d8a597490472594400c223`. You can just transfer to it by running: ```bash near tokens send-near 0861ea8ddd696525696ccf3148dd706c4fda981c64d8a597490472594400c223 ' NEAR' network-config testnet sign-with-keychain send ``` ## BONUS: Converting public key using python (for learning purposes) {#bonus-converting-public-key-using-python-for-learning-purposes} For this flow we'll use `python3` (with version `3.5+`) with `base58` library. You can install this library with `pip3`: ```bash pip3 install --user base58 ``` Start python3 interpreter: ```bash python3 ``` The first thing is to get the data part from the public key (without `ed25519:` prefix). Let's store it in a variable `pk58`: ```python pk58 = 'BGCCDDHfysuuVnaNVtEhhqeT4k9Muyem3Kpgq2U1m9HX' ``` Now let's import base58: ```python ``` Finally, let's convert our base58 public key representation to bytes and then to hex: ```python base58.b58decode(pk58).hex() ``` Output: ``` '98793cd91a3f870fb126f66285808c7e094afcfc4eda8a970f6648cdf0dbd6de' ``` This gives us the same account ID as `near-cli`, so this is encouraging. :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/tutorials/protocol/importing-account.md --- id: importing-account title: Importing a NEAR Account description: Learn how to import an existing NEAR account into a wallet or the CLI --- If you created a NEAR account using a wallet or the CLI, you can export its seed phrase (or private key) to use it in other wallets or tools. Let's cover how to: 1. [Exporting](#exporting-your-seed-phrase) an existing account 1. Import an existing account [into a wallet](#importing-into-a-wallet) 2. Import an existing account [into the CLI](#importing-into-the-cli) :::tip If you don't have a NEAR account yet, check out our guide on [Creating a NEAR Account](./create-account.md) ::: --- ## Exporting Your Seed Phrase You can export your seed phrase from the wallet you used to create your account. Most wallets will have an option to "Export Seed Phrase" or "Backup Account". Follow the instructions provided by your specific wallet. If you created your account using the CLI, you can export your seed phrase using the following command: ```bash near account export-account ``` --- ## Importing Into a Wallet To import an existing account into a wallet, follow the instructions provided by the specific wallet you are using. Most wallets will have an option to "Import Account" or "Restore from Seed Phrase" --- ## Importing Into the CLI To import an existing account into the CLI, you can use the following command: ```bash near account import-account ``` This command will prompt you to enter your seed phrase or private key. Once you enter it, the CLI will import your account and you can start using it. --- # Source: https://docs.near.org/data-infrastructure/indexers.md --- id: indexers title: Introduction to Indexers sidebar_label: Introduction description: "Learn about blockchain indexers, how they work with NEAR Protocol, the difference between pull and push models, and when to use indexers for data querying." --- Here you will find everything you need to know in order to familiarize yourself with the concept of indexers. :::info We recommend checking the [NEAR Data Flow](../protocol/data-flow/near-data-flow.md) to familiarize yourself with how data flows within the NEAR ecosystem ::: --- ## Blockchains and their nature Blockchain data is optimized for serialized **writes**, one block at a time, as the chain is being created. Querying the blockchain for data about a specific block or account is fairly straightforward, as only the data for a specific block needs to be retrieved. However, querying data across many blocks (e.g. `all transfers between date X and Y`) can be cumbersome because we have to aggregate results from multiple single-block queries. Given the fact that a blockchain itself is a distributed database, and a smart contract (decentralized application, dApp) is an application that runs on a virtual machine inside a blockchain, we need to understand that smart contracts should *not* be considered as a "backend". While some applications might consist only of smart contracts, building a dApp with only smart contracts, in most cases, is not possible. Smart contracts are limited in terms of interactions. By "interactions" we mean things that are very common in the real world, like user notifications, integration with third-party applications, etc. However, the nature of a blockchain is that it *must* be deterministic. A critical feature of a blockchain is that it knows the state at a given time, and for blockchains that time unit is a block. Think of them as being snapshots. A blockchain does snapshots of its state on every block. We as users can call smart contracts for a specific block, and the blockchain provides guarantees that execution will always produce the same result for the same block any time we call it. The deterministic nature of a blockchain closes it from external (off-chain) variables. It is totally impossible to perform a call to an API from within a smart contract. A blockchain and a smart contract are closed off from the external (off-chain) world. ![Blockchain closed from outer world](/assets/docs/data-infrastructure/blockchain.png) Blockchains are great at providing a way to apply the requested changes to the state in a decentralized manner. However, in order to observe the changes, you need to actively pull the information from the network. Instead of abstract explanations let's look at an example. :::note Example dApp Say, we have a smart contract that sells e-books. Once a user buys a book we want to send them a copy via email. ::: The dApp has a helper deployed somewhere off-chain, and this helper has code that can send an email with a copy of an e-book. But how would we trigger the helper? --- ## Getting the data from a blockchain from the external world NEAR blockchain implements a [JSON-RPC endpoint](/api/rpc/introduction) for everyone to interact with the blockchain. Through the JSON-RPC API users can call smart contracts triggering them to be executed with given parameters. Also, users can view the data from the blockchain. So, continuing with our example we can make our helper pull a [Block](/api/rpc/block-chunk#block-details) every second, then pull all the [Chunks](/api/rpc/block-chunk#chunk-details) and analyze the Transactions included in the Block to check if there is a transaction to our smart contract with "buy an e-book" function call. If we observe such a Transaction, we need to ensure it is successful, so we don't send the e-book to a user whose "buy e-book" Transaction failed. After the process is complete we can trigger the helper's code to send the user an email with the e-book they bought. This approach is so-called *pull model* of getting the data. There is nothing wrong with this approach, but sometimes you might find it is not the most comfortable or reliable approach. Also, not all the data is available through the JSON-RPC. *Local Receipts* for example are not available through the JSON-RPC, because they are not stored in NEAR node's internal database. --- ## Indexer A blockchain indexer is an implementation of the *push model* of getting the data. Instead of actively pulling the data from the source, your helper waits for the data to be sent to it. The data is complete and so the helper can start analyzing it immediately; ideally the data is complete enough to avoid additional pulls to get more details. Getting back to our example, the helper becomes **an indexer** that receives every *Block*, along with **Chunks**, **Transactions** with its statuses, etc. In the same way the helper analyzes the data and triggers the code to send the user an email with the e-book they bought. ![Indexer is streaming the data from the blockchain](/assets/docs/data-infrastructure/indexer.png) :::info An indexer concept An indexer listens to the *stream of data as it's being written on chain* and can then be immediately filtered and processed to detect interesting events or patterns. ::: --- ## Indexers and "wide" queries The term *"wide" queries* was mentioned in the beginning of this document. Here's a recap: :::note "Wide" queries definition To query data across many blocks requires the aggregation of results from multiple single-block queries. We can consider these aggregates as coming from *"wide" queries*. ::: Because indexers listen to the *stream of data* from the blockchain and the data can be immediately filtered and processed according to defined requirements, they can be used to simplify the "wide" queries execution. For example, a stream of data can be written to a permanent database for later data analysis using a convenient query language like SQL. Another example that highlights the need for a "wide query" is when you use a seed phrase to recover one or more accounts. Since a seed phrase essentially represents a signing key pair, the recovery is for all accounts that share the associated public key. Therefore, when a seed phrase is used to recover an account via [NEAR Wallet](https://wallet.near.org), the query requires that all accounts with a matching public key are found and recovered. Utilizing [Near Lake Framework](https://github.com/near/near-lake-framework-rs) can be used to store this data in a permanent database and this allows [NEAR Wallet](https://wallet.near.org) to perform such "wide queries". This is impossible to achieve using JSON-RPC only. --- ## Indexers in the NEAR ecosystem If you are ready to host your own indexer, we recommend using the [Near Lake Framework](./lake-framework/near-lake.md) as it is simple, reliable, and available in multiple languages (JavaScript, Rust, Python). If speed is critical for your indexing needs, consider using [Near Indexer](./near-indexer.md). However, please note that maintaining it can be more complex and costly, as it essentially operates as an independent node in the network. If you prefer not to host your own solution, you can utilize [third-party services](./data-services.md). --- ## Summary We hope this article gives you an understanding of the Indexer concept. Also, we hope now you can easily decide whether you need an indexer for your application. --- ## What's next? We encourage you to learn more about the [Lake Indexer project](./lake-framework/near-lake). Please, proceed to [Tutorials](./tutorials/state-changes) section to learn how to build an indexer on practice. Alternatively, there are a few other third-party indexers that are tightly integrated with the NEAR ecosystem. You can review all of your data sourcing options (including The Graph, Pagoda, Pikespeak, and SubQuery) under [indexing tools](./data-services.md). --- # Source: https://docs.near.org/tutorials/auction/indexing-historical-data.md --- id: indexing-historical-data title: Indexing Historical Data description: "Using data apis to retrieve the history of auctions" --- In our frontend, we can easily display the previous bid since it's stored in the contract's state. However, we're unable to see previous bids to the auction. An indexer is used to fetch historical data from the blockchain and store it in a database. Since indexers can take a while to set up and can be expensive to run, we will use a pre-defined API endpoint provided by NEAR Blocks to query an indexer they run that will fetch us the data we need. --- ## NEAR Blocks API key NEAR Blocks provides a free tier that allows you to make 6 calls per minute, which will be plenty for our use case. To get an API key, head over to https://dash.nearblocks.io/user/overview and sign up. Once signed go to `API Keys` then click `Add key` and give it whatever name you like. We'll create a new file named `.env.local` to store our API key. ```env API_KEY=YOUR_API_KEY_GOES_HERE ``` We put the API key in a `.env.local` file so the user cannot access it in the browser and use our key elsewhere. We should also add `.env.local` to our `.gitignore` file so it is not pushed to GitHub. --- ## Calling the API endpoint NextJS allows us to easily create server-side functions with API routes. We need to make this API call on the server-side rather than the client side so as to not expose our API key. We'll create a new file in src/pages/api named `getBidHistory.js`. Here we'll define our function to get the bid history. ``` if (!process.env.API_KEY) { return res.status(500).json({ error: "API key not provided" }); } // Get all bid transactions const { contractId } = req.query; const bidsRes = await fetch(`https://api-testnet.nearblocks.io/v1/account/${contractId}/txns?method=bid&page=1&per_page=25&order=desc`, { headers: { 'Accept': '*/*', 'Authorization': `Bearer ${process.env.API_KEY}` } }); ``` Here we are retrieving the auction contract ID from the API route call and then calling the NEAR Blocks API. This specific API endpoint allows us to retrieve transactions made to a specific contract calling a specific function. Some details are worth discussing here: - We pass the account ID of the auction contract, which is `basic-auction-example.testnet` in the example repo. - We specify the function name on the auction contract that we want the transactions for, in our case it will be `bid` - We'll receive a JSON object of up to 25 transactions, ordered by the most recent first. - We pass our API key to authenticate the request. --- ## Retrieving the bids from the API result From our API call, we receive a JSON object containing up to 25 transactions made to the bid function on the auction contract. ``` const bidsJson = await bidsRes.json(); const txns = bidsJson.txns; let pastBids = []; // Loop through all bids and add valid bids to the pastBids array until 5 are found for (let i = 0; i < txns.length; i++) { const txn = txns[i]; if (txn.receipt_outcome.status) { let amount = txn.actions[0].deposit; let account = txn.predecessor_account_id if (pastBids.length < 5) { pastBids.push([account, amount]); } else { break; } } } // Respond with the past bids return res.status(200).json({ pastBids }); } catch (error) { ``` We want to display the 5 most recent valid bids. To do this we loop through each transaction and check whether the transaction was successful by checking `receipt_outcome.status` is `true`. If so we check the first action (since there should only be one function call action in this case) and store the `deposit`, which is equal to the bid amount, and store the `predecessor account ID`, which is the account ID of the bidder. Once we have 5 valid bids we can stop looping through the transactions. Note that in our example if the previous 25 bids were invalid the API will return an empty array. The function could be set up such that it calls the API again to get the new page of transactions if this is the case. :::tip Learn More You can read more about transaction actions in this section of the documentation: [Actions](/protocol/transaction-anatomy#actions) ::: --- ## Using the API Route In our main page, we'll define a function to call the API route we just created. This function will be called each time the page timer reaches zero. ``` const fetchPastBids = async () => { const response = await fetch(`/api/getBidHistory?contractId=${AUCTION_CONTRACT}`); const data = await response.json(); if (data.error) { setPastBids(data.error); } else { setPastBids(data.pastBids); } } ``` The `pastBids` will then be passed into the `Bid` component to be displayed. --- You may like to explore NEAR Blocks APIs further to see what other data you can retrieve from the blockchain. You can find the documentation at https://api.nearblocks.io/api-docs/ --- ## Using the frontend Now we have implemented the frontend and indexer you can go ahead and actually use the frontend. From the root of the frontend directory run the following commands: ```bash # install dependencies npm install # run the frontend locally npm run dev ``` --- ## Conclusion In this short part of the tutorial, we've added the ability to display the previous 5 valid bids made to the auction contract. In doing this we learned how to interact with the NEAR Blocks APIs to retrieve historical data from the blockchain and how to make server-side calls in NextJS to not expose our API key. Now we have a pretty good frontend that displays all the information we need about the auction contract. In the [next section of the tutorial](./3.1-nft.md) we're going to improve our contract by adding primitives to the auction contract starting with adding NFTs as a prize. --- # Source: https://docs.near.org/smart-contracts/testing/integration-test.md --- id: integration-test title: Integration Tests description: "Learn how to write and run integration tests for NEAR smart contracts using Sandbox testing and realistic blockchain environments." --- Integration tests enable you to deploy a contract in the NEAR `testnet` or a local `sandbox` and create test users to interact with it. This way, you can thoroughly test your contract in a realistic environment. Moreover, when using the local `sandbox` you gain complete control of the network: 1. Create test `Accounts` and manipulate their `State` and `Balance`. 2. Simulate errors on callbacks. 3. Control the time-flow and fast-forward into the future (Rust ready, TS coming soon). In NEAR, integration tests are implemented using a framework called **Workspaces**. Workspaces comes in two flavors: [🦀 Rust](https://github.com/near/workspaces-rs) and [🌐 Typescript](https://github.com/near/workspaces-js). All of our [examples](https://github.com/near-examples) come with integration testing. :::note Sandbox Testing NEAR Workspaces allows you to write tests once, and run them either on `testnet` or a local `Sandbox`. By **default**, Workspaces will start a **sandbox** and run your tests **locally**. Lets dive into the features of our framework and see how they can help you. ::: --- ## Create Accounts ### Dev Account ``` let sandbox = near_workspaces::sandbox().await?; let dev_account = sandbox.dev_create_account().await?; println!("dev_account: {:#?}", dev_account); // dev_account: Account { id: AccountId("dev-20250131121318-56660632612680") } ``` ``` const { root } = t.context.accounts; const devUser = await root.devCreateAccount(); console.log('devUser:', devUser); // devUser: Account { // _accountId: 'dev-32502-22496.test.near', // manager: SandboxManager { // config: { // network: 'sandbox', // rootAccountId: 'test.near', // rpcAddr: 'http://127.0.0.1:21879', // initialBalance: '100000000000000000000000000', // homeDir: '/private/var/folders/yf/n8735nxj4qz69g1ck3zchh680000gn/T/sandbox/bd47fd10-f3ad-4253-a828-47a599de2bbd', // port: 21879, // rm: false, // refDir: null // }, // accountsCreated: Set(2) { 'test-account.test.near', 'dev-32502-22496.test.near' }, // _root: Account { _accountId: 'test.near', manager: [Circular *1] } // } // } ```
### Subaccount ``` let sandbox = near_workspaces::sandbox().await?; let root_account = sandbox.root_account().unwrap(); println!("root_account: {:#?}", root_account); // root_account: Account { id: AccountId("test.near") } let alice_account = root_account .create_subaccount("alice") .initial_balance(NearToken::from_near(1)) .transact() .await? .into_result()?; println!("alice_account: {:#?}", alice_account); // alice_account: Account { id: AccountId("alice.test.near") } ``` ``` const { root } = t.context.accounts; const subaccount = await root.createSubAccount('subaccount'); console.log('subaccount:', subaccount); // subaccount: Account { // _accountId: 'subaccount.test.near', // manager: SandboxManager { // config: { // network: 'sandbox', // rootAccountId: 'test.near', // rpcAddr: 'http://127.0.0.1:5014', // initialBalance: '100000000000000000000000000', // homeDir: '/private/var/folders/yf/n8735nxj4qz69g1ck3zchh680000gn/T/sandbox/f6ebeb0b-ecb9-4233-b6df-facbb9861b15', // port: 5014, // rm: false, // refDir: null // }, // accountsCreated: Set(2) { 'test-account.test.near', 'subaccount.test.near' }, // _root: Account { _accountId: 'test.near', manager: [Circular *1] } // } // } ```
### Using Secret Key ``` let sandbox = near_workspaces::sandbox().await?; let account_id: AccountId = "test-account.near".parse().unwrap(); let secret_key = SecretKey::from_random(KeyType::ED25519); let account = Account::from_secret_key(account_id, secret_key, &sandbox); println!("account: {:#?}", account); // account: Account { id: AccountId("test-account.near") } ``` ``` const { root } = t.context.accounts; const account_id = "secret.test.near"; const keyPair = KeyPair.fromRandom("ED25519"); const account = await root.createAccount(account_id, { keyPair, initialBalance: "100000000000000000000000", }); console.log('account from secret key:', account); // account from secret key: Account { // _accountId: 'secret.test.near', // manager: SandboxManager { // config: { // network: 'sandbox', // rootAccountId: 'test.near', // rpcAddr: 'http://127.0.0.1:28270', // initialBalance: '100000000000000000000000000', // homeDir: '/private/var/folders/yf/n8735nxj4qz69g1ck3zchh680000gn/T/sandbox/04b4f82d-6232-4a87-8c7d-3396f1c3eb57', // port: 28270, // rm: false, // refDir: null // }, // accountsCreated: Set(2) { 'test-account.test.near', 'secret.test.near' }, // _root: Account { _accountId: 'test.near', manager: [Circular *1] } // } // } ```
### Using Credentials From File ``` let sandbox = near_workspaces::sandbox().await?; let account = Account::from_file("./tests/credentials.json", &sandbox)?; println!("account: {:#?}", account); // account: Account { id: AccountId("test-ac-1719933221123-3.testnet") } ``` ``` const { root } = t.context.accounts; const account_id = "file.test.near"; const keyPair = await getKeyFromFile('.near-credentials/workspaces/testnet/getKeyFromFile.json'); const account = await root.createAccount(account_id, { keyPair, initialBalance: "100000000000000000000000", }); console.log('account with credentials from file:', account); // account with credentials from file: Account { // _accountId: 'file.test.near', // manager: SandboxManager { // config: { // network: 'sandbox', // rootAccountId: 'test.near', // rpcAddr: 'http://127.0.0.1:59952', // initialBalance: '100000000000000000000000000', // homeDir: '/private/var/folders/yf/n8735nxj4qz69g1ck3zchh680000gn/T/sandbox/f8b61465-4fb7-48ae-9005-fcaa79d6ab62', // port: 59952, // rm: false, // refDir: null // }, // accountsCreated: Set(2) { 'test-account.test.near', 'file.test.near' }, // _root: Account { _accountId: 'test.near', manager: [Circular *1] } // } // } ``` --- ## WASM Files ### Compile Contract Code ``` let compiling_wasm_result = near_workspaces::compile_project("./").await; assert!(compiling_wasm_result.is_ok()); let _contract_wasm = compiling_wasm_result.expect("Could not process WASM file after compiling"); ``` :::tip You don't need to assert compiling process everytime. You can use `?` operator to get the result as `Vec` without dealing with `Result>, Error>` type. That way you can directly use this vector to deploy the wasm file into account. Your test will still fail if compiling process fails. ```rust let contract_wasm = near_workspaces::compile_project("./").await?; ``` ::: If you want to compile a contract each time running tests, you can put following scripts into `package.json` file. In the code you can access path to compiled file using `process.argv[2]`. `package.json` file: ```json "scripts": { "build": "near-sdk-js build src/contract.ts build/hello_near.wasm", "test": "$npm_execpath run build && ava -- ./build/hello_near.wasm" }, ``` `main.ava.js` file: ```js const pathToWasm = process.argv[2]; await contract.deploy(pathToWasm); ```
### Loading From File ``` let artifact_path = "target/near/hello_rs.wasm"; let reading_file_result = std::fs::read(artifact_path); assert!(reading_file_result.is_ok()); let _contract_wasm = reading_file_result .expect(format!("Could not read WASM file from {}", artifact_path).as_str()); ``` :::tip The same as in the case of compilation wasm from code, you don't need to assert reading file process everytime. You can use `expect` method to get the reading file result as `Vec` and provide error message as a parameter. Your test will still fail if compiling process fails. ```rust let contract_wasm = std::fs::read(artifact_path) .expect(format!("Could not read WASM file from {}", artifact_path).as_str()); ``` ::: If you want to use pre-compiled a contract, you can put following scripts into `package.json` file. In the code you can access path to pre-compiled file using `process.argv[2]`. `package.json` file: ```json "scripts": { "build": "near-sdk-js build src/contract.ts build/hello_near.wasm", "test": "ava -- ./build/hello_near.wasm" }, ``` `main.ava.js` file: ```js const pathToWasm = process.argv[2]; await contract.deploy(pathToWasm); ``` --- ## Deploy Contracts ### Dev Deploy ``` let sandbox = near_workspaces::sandbox().await?; let wasm_file = near_workspaces::compile_project("./").await?; let contract = sandbox.dev_deploy(&wasm_file).await?; println!("contract: {:#?}", contract); // contract: Contract { id: AccountId("dev-20250131125513-33446418241044") } ``` ``` const { root } = t.context.accounts; const contract = await root.devDeploy('./build/hello_near.wasm'); console.log('contract:', contract); // contract: Account { // _accountId: 'dev-5878-35168.test.near', // manager: SandboxManager { // config: { // network: 'sandbox', // rootAccountId: 'test.near', // rpcAddr: 'http://127.0.0.1:42499', // initialBalance: '100000000000000000000000000', // homeDir: '/private/var/folders/yf/n8735nxj4qz69g1ck3zchh680000gn/T/sandbox/f5106fc1-2495-48dc-8db3-b4b587215109', // port: 42499, // rm: false, // refDir: null // }, // accountsCreated: Set(2) { 'test-account.test.near', 'dev-5878-35168.test.near' }, // _root: Account { _accountId: 'test.near', manager: [Circular *1] } // } // } ```
### Deploy To Account ``` let sandbox = near_workspaces::sandbox().await?; let wasm_file = near_workspaces::compile_project("./").await?; let account = sandbox.dev_create_account().await?; let contract = account.deploy(&wasm_file).await?.unwrap(); println!("contract: {:#?}", contract); // contract: Contract { id: AccountId("dev-20250131125513-33446418241044") } ``` ``` const root = worker.rootAccount; const contract = await root.createSubAccount('test-account'); // Get wasm file path from package.json test script in folder above await contract.deploy( process.argv[2], ); ``` --- ## Logs Show contract's logs. You can use `println` or `dbg!` when you want to see information from your code. ``` let sandbox = near_workspaces::sandbox().await?; let dev_account = sandbox.dev_create_account().await?; println!("dev_account: {:#?}", dev_account); // dev_account: Account { id: AccountId("dev-20250131121318-56660632612680") } ``` In Rust, the output from your code is captured by default and not displayed in the terminal. In order to see the output, you have to use the `--nocapture` flag eg. `cargo test -- --nocapture` If you want to access the contracts logs, you can find them in the `tx_outcome.logs()` Vec. ```rust let tx_outcome = user_account .call(contract.id(), "set_greeting") .args_json(json!({"greeting": "Hello World!"})) .transact() .await?; assert!(tx_outcome.is_success()); dbg!(tx_outcome.logs()); // [tests/test_basics.rs:29:5] tx_outcome.logs() = [ // "Saving greeting: Hello World!", // ] ``` Use `console.log` method when you want to see debug information from your code. ```js const balance = await account.balance(); console.log('balance: ', balance); // balance: { // total: , // stateStaked: , // staked: , // available: // } ``` --- ## Account Balance ``` let sandbox = near_workspaces::sandbox().await?; let account = sandbox.dev_create_account().await?; let account_details = account .view_account() .await .expect("Account has to have some balance"); println!("account_details: {:#?}", account_details); // account_details: AccountDetails { balance: NearToken { inner: 100000000000000000000000000 }, locked: NearToken { inner: 0 }, code_hash: 11111111111111111111111111111111, storage_usage: 182, storage_paid_at: 0 } ``` ``` const { root } = t.context.accounts; const account = await root.devCreateAccount(); const balance = await account.balance(); console.log('balance:', balance.total.toHuman()); // balance: { // total: , // stateStaked: , // staked: , // available: // } ``` --- ## Transactions ### Call ``` let sandbox = near_workspaces::sandbox().await?; let ft_wasm = near_workspaces::compile_project("./tests/contracts/ft").await?; let ft_contract = sandbox.dev_deploy(&ft_wasm).await?; let initialize_result = ft_contract .call("new_default_meta") .args_json(json!({"owner_id": ft_contract.id(), "name": "token", "symbol": "tt", "total_supply": "1000000000000000000000000" })) .gas(Gas::from_tgas(100)) .transact() .await?; println!("initialize_result: {:#?}", initialize_result); // initialize_result: ExecutionFinalResult { total_gas_burnt: NearGas { inner: 2225558148031 }, transaction: ExecutionOutcome { transaction_hash: 9NhQxh78Q7bdnYeYr7ccmhkb9z9iBnyf9JyoL4PF5uUb, block_hash: 4iQtc95L2SmyoU3XdxtcHQQURzbQJod6VFSgYZvYKwBy, logs: [], receipt_ids: [3qtZNe36azn73j2ndQNWNL4kDEeMBn3fQiBunAokoaop], gas_burnt: NearGas { inner: 308363587024 }, tokens_burnt: NearToken { inner: 30836358702400000000 }, executor_id: AccountId("dev-20250131130629-49143087716943"), status: SuccessReceiptId(3qtZNe36azn73j2ndQNWNL4kDEeMBn3fQiBunAokoaop) }, receipts: [ExecutionOutcome { transaction_hash: 3qtZNe36azn73j2ndQNWNL4kDEeMBn3fQiBunAokoaop, block_hash: 4iQtc95L2SmyoU3XdxtcHQQURzbQJod6VFSgYZvYKwBy, logs: ["EVENT_JSON:{\"standard\":\"nep141\",\"version\":\"1.0.0\",\"event\":\"ft_mint\",\"data\":[{\"owner_id\":\"dev-20250131130629-49143087716943\",\"amount\":\"1000000000000000000000000\",\"memo\":\"new tokens are minted\"}]}"], receipt_ids: [FrSvWZWMUCZbiuGrmkj19UTciXMTZgenZmJmLkUPt1AK], gas_burnt: NearGas { inner: 1917194561007 }, tokens_burnt: NearToken { inner: 191719456100700000000 }, executor_id: AccountId("dev-20250131130629-49143087716943"), status: SuccessValue('') }], status: SuccessValue('') } ``` ``` const { root, contract } = t.context.accounts; await root.call(contract, 'set_greeting', { greeting: 'Howdy' }); const greeting = await contract.view('get_greeting', {}); ```
### View ``` let sandbox = near_workspaces::sandbox().await?; let ft_wasm = near_workspaces::compile_project("./tests/contracts/ft").await?; let ft_contract = sandbox.dev_deploy(&ft_wasm).await?; let initialize_result = ft_contract .call("new_default_meta") .args_json(json!({"owner_id": ft_contract.id(), "name": "token", "symbol": "tt", "total_supply": "1000000000000000000000000" })) .gas(Gas::from_tgas(100)) .transact() .await?; assert!(initialize_result.is_success()); let view_transaction_result = ft_contract .call("ft_balance_of") .args_json((ft_contract.id(),)) .view() .await?; println!("view_transaction_result: {:#?}", view_transaction_result); // ViewResultDetails { result: [34, 49, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 34], logs: [] } let account_balance = view_transaction_result.json::()?; println!("account_balance: {:#?}", account_balance); // account_details: AccountDetails { balance: NearToken { inner: 100000000000000000000000000 }, locked: NearToken { inner: 0 }, code_hash: 11111111111111111111111111111111, storage_usage: 182, storage_paid_at: 0 } ``` ``` const { contract } = t.context.accounts; const greeting = await contract.view('get_greeting', {}); t.is(greeting, 'Hello'); ``` --- ## Patch State on the Fly In Sandbox-mode, you can add or modify any contract state, contract code, account or access key with `patchState`. You can alter contract code, accounts, and access keys using normal transactions via the `DeployContract`, `CreateAccount`, and `AddKey` [actions](https://nomicon.io/RuntimeSpec/Actions#addkeyaction). But this limits you to altering your own account or sub-account. `patchState` allows you to perform these operations on any account. ``` let sandbox = near_workspaces::sandbox().await?; let wasm_file = near_workspaces::compile_project("./").await?; let contract = sandbox.dev_deploy(&wasm_file).await?; let new_greeting = "Howdy"; let _ = sandbox .patch_state( contract.id(), "STATE".as_bytes(), &borsh::to_vec(new_greeting)?, ) .await?; let current_greeting = contract .call("get_greeting") .view() .await? .json::()?; println!("current_greeting: {:#?}", current_greeting); // current_greeting: "Howdy" ``` ``` const { contract } = t.context.accounts; const greeting = await contract.view('get_greeting', {}); t.is(greeting, 'Hello'); const new_greeting = 'Howdy'; await contract.patchState('STATE', JSON.stringify({"greeting": new_greeting})); const updated_greeting = await contract.view('get_greeting', {}); console.log('updated_greeting:', updated_greeting); // updated_greeting: Howdy ``` To see a complete example of how to do this, see the [patch-state test](https://github.com/near/workspaces-js/blob/main/__tests__/02.patch-state.ava.ts). :::note As an alternative to `patchState`, you can stop the node, dump state at genesis, edit the genesis, and restart the node. This approach is more complex to do and also cannot be performed without restarting the node. ::: --- ## Time Traveling `workspaces` offers support for forwarding the state of the blockchain to the future. This means contracts which require time sensitive data do not need to sit and wait the same amount of time for blocks on the sandbox to be produced. We can simply just call `worker.fast_forward` to get us further in time: ``` let sandbox = near_workspaces::sandbox().await?; let wasm_file = near_workspaces::compile_project("./tests/contracts/simple-contract").await?; let contract = sandbox.dev_deploy(&wasm_file).await?; let (timestamp, epoch_height): (u64, u64) = contract.call("current_env_data").view().await?.json()?; println!("timestamp = {}, epoch_height = {}", timestamp, epoch_height); let block_info = sandbox.view_block().await?; println!("BlockInfo pre-fast_forward {:#?}", block_info); // Call into fast_forward. This will take a bit of time to invoke, but is // faster than manually waiting for the same amounts of blocks to be produced sandbox.fast_forward(10000).await?; let (timestamp, epoch_height): (u64, u64) = contract.call("current_env_data").view().await?.json()?; println!("timestamp = {}, epoch_height = {}", timestamp, epoch_height); let block_info = sandbox.view_block().await?; println!("BlockInfo post-fast_forward {:#?}", block_info); ``` _[See the full example on Github](https://github.com/near/workspaces-rs/blob/main/examples/src/fast_forward.rs)._ ``` const { root } = t.context.accounts; const { worker } = t.context; const simpleContract = await root.devDeploy('./sandbox-test/contracts/simple_contract.wasm'); const [initialTimestamp, initialEpochHeight] = await simpleContract.view('current_env_data', {}); console.log(`initialTimestamp = ${initialTimestamp}, initialEpochHeight = ${initialEpochHeight}`); const initialBlockInfo = await worker.provider.block({ finality: 'final' }); console.log('initialBlockInfo:', initialBlockInfo); const delta = 10000; await worker.provider.fastForward(delta); const [finalTimestamp, finalEpochHeight] = await simpleContract.view('current_env_data', {}); console.log(`finalTimestamp = ${finalTimestamp}, finalEpochHeight = ${finalEpochHeight}`); const finalBlockInfo = await worker.provider.block({ finality: 'final' }); console.log('finalBlockInfo:', finalBlockInfo); // Rounding off to nearest hundred, providing wiggle room incase not perfectly `forward_height` t.true(Math.ceil(finalBlockInfo.header.height / 100) * 100 === delta); }); ``` --- ## Using Testnet NEAR Workspaces is set up so that you can write tests once and run them against a local Sandbox node (the default behavior) or against [NEAR TestNet](../../protocol/network/networks.md). Some reasons this might be helpful: * Gives higher confidence that your contracts work as expected * You can test against deployed testnet contracts * If something seems off in Sandbox mode, you can compare it to testnet ``` let testnet = near_workspaces::testnet().await?; let account_id: AccountId = "test-ac-1719933221123-3.testnet".parse().unwrap(); let secret_key: SecretKey = "ed25519:4ERg5chhrvzbqv4jUzbcSwejcEzzqaqxF5NL9RhPV3X9MF5pJjrSeWPMic8QcJaJz8mL7xHqgyQxZoHn6XJWstQe".parse().unwrap(); let account = Account::from_secret_key(account_id, secret_key, &testnet); println!("account: {:#?}", account); // account: Account { id: AccountId("test-ac-1719933221123-3.testnet") } assert_eq!(account.id(), "test-ac-1719933221123-3.testnet"); let hello_near_id: AccountId = "hello.near-examples.testnet".parse().unwrap(); let current_greeting_on_testnet = account .call(&hello_near_id, "get_greeting") .view() .await? .json::() .unwrap(); println!( "current_greeting_on_testnet: {:#?}", current_greeting_on_testnet ); // current_greeting_on_testnet: "defi is cool" ``` :::tip If you can create a new account on each iteration as well. ::: You can switch to testnet mode in three ways:
1. Setting the `Worker` network to `testnet` When creating Worker set network to `testnet` and pass your master account (an account for which you have the private key): ```ts const worker = await Worker.init({ network: 'testnet', testnetMasterAccountId: '', initialBalance: NEAR.parse(" N").toString(), }) ```
2. Setting environment variables Set the `NEAR_WORKSPACES_NETWORK` and `TESTNET_MASTER_ACCOUNT_ID` (an account for which you have the private key) environment variables when running your tests: ```bash NEAR_WORKSPACES_NETWORK=testnet TESTNET_MASTER_ACCOUNT_ID= node test.js ``` If you set this environment variables and pass `{network: 'testnet', testnetMasterAccountId: }` to `Worker.init`, the config object takes precedence.
3. Config file If you are using AVA, you can use a custom config file. Other test runners allow similar config files; adjust the following instructions for your situation. Create a file in the same directory as your `package.json` called `ava.testnet.config.cjs` with the following contents: ```js module.exports = { ...require('near-workspaces/ava.testnet.config.cjs'), ...require('./ava.config.cjs'), }; module.exports.environmentVariables = { TESTNET_MASTER_ACCOUNT_ID: '', }; ``` Where the master account is an account for which you have the private key. The [near-workspaces/ava.testnet.config.cjs](https://github.com/near/workspaces-js/blob/main/ava.testnet.config.cjs) import sets the `NEAR_WORKSPACES_NETWORK` environment variable for you. A benefit of this approach is that you can then easily ignore files that should only run in Sandbox mode. Now you'll also want to add a `test:testnet` script to your `package.json`'s `scripts` section: ```diff "scripts": { "test": "ava", + "test:testnet": "ava --config ./ava.testnet.config.cjs" } ```
To use the accounts, you will need to create the `.near-credentials/workspaces/testnet` directory and add files for your master account, for example: ```js // .near-credentials/workspaces/testnet/.testnet.json {"account_id":".testnet","public_key":"ed25519:...","private_key":"ed25519:..."} ``` Example: ``` const testnet = await Worker.init({ network: 'testnet', testnetMasterAccountId: 'test-ac-1719933221123-3.testnet', initialBalance: NEAR.parse("0.1 N").toString()}); const currentGreetingOnTestnet = await testnet.rootAccount.call('hello.near-examples.testnet', 'get_greeting', {}); console.log('currentGreetingOnTestnet:', currentGreetingOnTestnet); const newGreeting = `howdy`; await testnet.rootAccount.call('hello.near-examples.testnet', 'set_greeting', { greeting: newGreeting }); const updatedGreetingOnTestnet = await testnet.rootAccount.call('hello.near-examples.testnet', 'get_greeting', {}); console.log('updatedGreetingOnTestnet:', updatedGreetingOnTestnet); t.is(updatedGreetingOnTestnet, newGreeting); }); ```
--- ## Spooning Contracts [Spooning a blockchain](https://coinmarketcap.com/alexandria/glossary/spoon-blockchain) is copying the data from one network into a different network. NEAR Workspaces makes it easy to copy data from Mainnet or Testnet contracts into your local Sandbox environment: Specify the contract name from `testnet` you want to be pulling, and a specific block ID referencing back to a specific time. (Just in case the contract you're referencing has been changed or updated) Create a function called `pull_contract` which will pull the contract's `.wasm` file from the chain and deploy it onto your local sandbox. You'll have to re-initialize it with all the data to run tests. This is because the contract's data is too big for the RPC service to pull down. (limits are set to 50Mb) ``` let sandbox = near_workspaces::sandbox().await?; let testnet = near_workspaces::testnet_archival().await?; const BLOCK_HEIGHT: BlockHeight = 186705486; let hello_near_id: AccountId = "hello.near-examples.testnet".parse().unwrap(); let sandbox_contract = sandbox .import_contract(&hello_near_id, &testnet) .block_height(BLOCK_HEIGHT) .initial_balance(NearToken::from_near(10)) .transact() .await?; let greeting = sandbox_contract .call("get_greeting") .view() .await? .json::() .unwrap(); println!("greeting: {:#?}", greeting); // greeting: "Hello" ``` ```ts const refFinance = await root.importContract({ mainnetContract: 'v2.ref-finance.near', blockId: 50_000_000, withData: true, }); ``` This would copy the Wasm bytes and contract state from [v2.ref-finance.near](https://nearblocks.io/address/v2.ref-finance.near) to your local blockchain as it existed at block `50_000_000`. This makes use of Sandbox's special [patch state](#patch-state-on-the-fly) feature to keep the contract name the same, even though the top level account might not exist locally (note that this means it only works in Sandbox testing mode). You can then interact with the contract in a deterministic way the same way you interact with all other accounts created with near-workspaces. :::note `withData` will only work out-of-the-box if the contract's data is 50kB or less. This is due to the default configuration of RPC servers; see [the "Heads Up" note here](/api/rpc/contracts#view-contract-state). ::: See a [TypeScript example of spooning](https://github.com/near/workspaces-js/blob/main/__tests__/05.spoon-contract-to-sandbox.ava.ts) contracts. --- ## Snippets ### Snippet I: Testing Hello NEAR Lets take a look at the test of our [Quickstart Project](../quickstart.md) [👋 Hello NEAR](https://github.com/near-examples/hello-near-examples), where we deploy the contract on an account and test it correctly retrieves and sets the greeting. ``` use near_api::{AccountId, NearGas, NearToken}; use near_sdk::serde_json::json; const FIVE_NEAR: NearToken = NearToken::from_near(5); #[tokio::test] async fn test_contract_is_operational() -> testresult::TestResult<()> { // Build the contract wasm file let contract_wasm_path = cargo_near_build::build_with_cli(Default::default())?; let contract_wasm = std::fs::read(contract_wasm_path)?; // Initialize the sandbox let sandbox = near_sandbox::Sandbox::start_sandbox().await?; let sandbox_network = near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?); // Create accounts let user_account = create_subaccount(&sandbox, "user.sandbox").await?; let contract = create_subaccount(&sandbox, "contract.sandbox") .await? .as_contract(); // Initialize signer for the contract deployment let signer = near_api::Signer::from_secret_key( near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY .parse() .unwrap(), )?; // Deploy the contract with the init call near_api::Contract::deploy(contract.account_id().clone()) .use_code(contract_wasm) .without_init_call() ``` ``` test.beforeEach(async t => { // Create sandbox const worker = t.context.worker = await Worker.init(); // Deploy contract const root = worker.rootAccount; const contract = await root.createSubAccount('test-account'); // Get wasm file path from package.json test script in folder above await contract.deploy( process.argv[2], ); // Save state for test runs, it is unique for each test t.context.accounts = { root, contract }; }); test.afterEach.always(async (t) => { await t.context.worker.tearDown().catch((error) => { console.log('Failed to stop the Sandbox:', error); }); }); test('returns the default greeting', async (t) => { const { contract } = t.context.accounts; const greeting = await contract.view('get_greeting', {}); t.is(greeting, 'Hello'); }); test('changes the greeting', async (t) => { const { root, contract } = t.context.accounts; await root.call(contract, 'set_greeting', { greeting: 'Howdy' }); const greeting = await contract.view('get_greeting', {}); t.is(greeting, 'Howdy'); }); ```
### Snippet II: Testing Donations In most cases we will want to test complex methods involving multiple users and money transfers. A perfect example for this is our [Donation Example](https://github.com/near-examples/donation-examples), which enables users to `donate` money to a beneficiary. Lets see its integration tests ``` use near_sdk::{json_types::{U128, U64}, AccountId}; use near_workspaces::types::NearToken; use serde_json::json; const ONE_NEAR: NearToken = NearToken::from_near(1); const STORAGE_COST: NearToken = NearToken::from_millinear(1); #[tokio::test] async fn main() -> Result<(), Box> { let sandbox = near_workspaces::sandbox().await?; let contract_wasm = near_workspaces::compile_project("./").await?; let contract = sandbox.dev_deploy(&contract_wasm).await?; let alice_account = sandbox.dev_create_account().await?; let bob_account = sandbox.dev_create_account().await?; let beneficiary_account = sandbox.dev_create_account().await?; let initial_balance = beneficiary_account.view_account().await?.balance; let outcome_init = contract .call("init") .args_json(json!({"beneficiary": beneficiary_account.id()})) .transact() .await?; assert!(outcome_init.is_success()); let alice_first_donation_outcome = alice_account .call(contract.id(), "donate") .args_json({}) .deposit(ONE_NEAR) .transact() .await?; assert!(alice_first_donation_outcome.is_success()); let bob_first_donation_outcome = bob_account .call(contract.id(), "donate") .args_json({}) .deposit(ONE_NEAR) .transact() .await?; assert!(bob_first_donation_outcome.is_success()); let _ = alice_account .call(contract.id(), "donate") .args_json({}) .deposit(ONE_NEAR.saturating_mul(3)) .transact() .await? .into_result(); let number_of_donors: U64 = contract .view("number_of_donors") .args_json({}) .await? .json()?; #[derive(near_sdk::serde::Serialize, near_sdk::serde::Deserialize, Debug, PartialEq)] #[serde(crate = "near_sdk::serde")] struct Donation { account_id: AccountId, total_amount: U128, } let donation: Donation = contract .view("get_donation_for_account") .args_json(json!({"account_id": alice_account.id()})) .await? .json()?; assert_eq!(number_of_donors, U64::from(2)); assert_eq!(u128::from(donation.total_amount), NearToken::from_near(4).as_yoctonear()); let donation_vec: Vec = contract .view("get_donations") .args_json(json!({})) .await? .json()?; assert_eq!( donation_vec, vec![ Donation { account_id: alice_account.id().clone(), total_amount: U128::from(NearToken::from_near(4).as_yoctonear()), }, Donation { account_id: bob_account.id().clone(), total_amount: U128::from(NearToken::from_near(1).as_yoctonear()), }, ] ); // total donation amount excluding the costs necesseary for storage let donation_amount = NearToken::from_near(5).saturating_sub(STORAGE_COST.saturating_mul(2)); let expected_balance = initial_balance.saturating_add(donation_amount); assert_eq!( beneficiary_account.view_account().await?.balance, expected_balance ); Ok(()) } ``` ``` test("sends donations to the beneficiary", async (t) => { const { contract, alice, beneficiary } = t.context.accounts; const balance = await beneficiary.balance(); const available = parseFloat(balance.available.toHuman()); await alice.call(contract, "donate", {}, { attachedDeposit: NEAR.parse("1 N").toString() }); const new_balance = await beneficiary.balance(); const new_available = parseFloat(new_balance.available.toHuman()); t.is(new_available, available + 1 - 0.001); }); test("records the donation", async (t) => { const { contract, bob } = t.context.accounts; await bob.call(contract, "donate", {}, { attachedDeposit: NEAR.parse("2 N").toString() }); /** @type {Donation} */ const donation = await contract.view("get_donation_for_account", { account_id: bob.accountId }); t.is(donation.account_id, bob.accountId); t.is(donation.total_amount, NEAR.parse("2 N").toString()); }); ``` --- ## Additional Resources ### Advanced Examples - [Rust](https://github.com/near/near-workspaces-rs/tree/main/examples) - [JavaScript](https://github.com/near/near-workspaces-js/tree/main/__tests__)
### Test Driven Design Using Workspaces and AVA {#test-driven-design} The video below walks through how to apply TDD with Workspaces and AVA for a simple contract: --- # Source: https://docs.near.org/integrations/errors/introduction.md # Source: https://docs.near.org/api/rpc/introduction.md # Source: https://docs.near.org/tutorials/multichain-dao/introduction.md # Source: https://docs.near.org/tutorials/controlling-near-accounts/introduction.md # Source: https://docs.near.org/tutorials/auction/introduction.md # Source: https://docs.near.org/primitives/lockup/introduction.md # Source: https://docs.near.org/web3-apps/tutorials/localnet/introduction.md # Source: https://docs.near.org/smart-contracts/testing/introduction.md # Source: https://docs.near.org/ai/shade-agents/getting-started/introduction.md # Source: https://docs.near.org/ai/introduction.md --- id: introduction title: AI and NEAR sidebar_label: Introduction description: "Introduction to NEAR's User-Owned AI vision, featuring Shade Agents and NEAR AI." --- Building AI agents that control accounts and assets on all blockchains has never been easier. NEAR enables AI agents to transact on any chain, while our **Shade Agent Framework** allows to create verifiable and trustless AI agents with multi-chain capabilities out of the box. ![img](/assets/docs/welcome-pages/9.near-nodes.png) :::tip Using AI to build on NEAR? Searching for how to use AI to help you build NEAR dApps, check our [Building NEAR Apps with AI](./llms.md) documentation ::: --- ## Let Your Agent use NEAR AI Agents can easily control account and assets on all blockchains thanks to NEARs unique features. You simply need to give them access to a NEAR account and they can start interacting with smart contracts, sending transactions, and managing assets everywhere. Check our [NEAR MCP](./near-mcp.md) documentation to get started. --- ## Build Trustless Multi-Chain Agents The [Shade Agent Framework](./shade-agents/getting-started/introduction.md) enables to build **verifiable** **multi-chain AI agents**. These agents operate in Trusted Execution Environments (TEEs) and leverage NEAR features such as multi-key management and Chain Signatures to securely manage assets and sign transactions across multiple blockchains. Shade agents can autonomously sign transactions on any chain, interact with AI models and external data sources, and perform privacy-preserving, verifiable computations. :::info Shade Agents power [Agentic Protocols](./shade-agents/concepts/what-can-you-build#agentic-protocols): a new type of decentralized application designed to be autonomous, proactive, and intelligent. ::: --- ## Which Docs Should I Use? | Docs | Best if you... | |--------------------------------------------------------------------------------|------------------------------------------------------------------------------------------| | [AI + NEAR](./near-mcp.md) | Already have an AI Agent, and want to connect it to NEAR (and other chains) | | [Building NEAR Apps with AI](./llms.md) | Are building a NEAR App, and want your Code Agents to help you better | | [Trustless Multi-chain Agents](./shade-agents/getting-started/introduction.md) | Are building an agent from zero and need to securely handle funds across multiple chains | --- ## Common Questions ### Can Trustless Agents lose access to their wallets? Not with Shade Agents. They use decentralized key management—any instance of the agent with the same code can access the same accounts. ### Do I need to know blockchain development? Our tools and resources help to abstract away the complexity of blockchain development. ### What chains can my agent interact with? NEAR accounts allow to transact on all chains, including Bitcoin, Ethereum and Solana thanks to Chain Signatures. --- # Source: https://docs.near.org/data-infrastructure/tutorials/running-near-lake/lake-start-options.md --- title: Start options id: lake-start-options description: "Learn how to create an indexer using the NEAR Lake Framework." --- This tutorial will guide you through creating a simple indexer using the NEAR Lake Framework that can start from a specified block height, the latest final block, or the last indexed block. ### Start Options There are three options to start an indexer using NEAR Lake Framework: - from specified block height (out of the box) ```bash ./target/release/indexer mainnet from-block 65359506 ``` - from the latest final block from the network ```bash ./target/release/indexer mainnet from-latest ``` - from the block indexer has indexed the last before it was interrupted ```bash ./target/release/indexer mainnet from-interruption ``` --- ## Motivation To find out whether you need an indexer for you project and to create one means you're covering only one side of things - the development. There is another important side - the maintenance. This involves: - indexer needs to be upgraded with a newer version of dependencies - indexer needs to be updates with a new features you've made - your server needs some maintenance - incident had happened - etc. Almost in all of the above cases you might want to start or restart your indexer not only from the specific block you need to provide, but from the block it was stopped, or from the latest final block in the network. [NEAR Lake Framework](/data-infrastructure/near-lake-framework) doesn't provide such options. Actually, we didn't empower the library with these options to start indexer intentionally. :::info Intent We want to keep [NEAR Lake Framework](/data-infrastructure/near-lake-framework) crate in the narrowest possible way. The goal for the library is to do a single job and allow it to be empowered with any features but outside of the crate itself ::: Though, the possibility to start indexer from the latest block or from the block after the one it has indexed the last, might be very useful. Also, during [the April Data Platform Community Meeting](https://github.com/near/indexers-docs/blob/main/blog/2022-05-11-community-meeting-record.mdx) we had a question whether we plan to add this feature to the library. We've promised to create a tutorial showing how to do it by your own. So here it is. --- ## Preparation In this tutorial we're not going to focus our attention on the indexer itself, but on the start options instead. :::note To simplify the code samples in the tutorial, we're writing entire application in a single file `src/main.rs`. **Please, do not take it as a design advice. We do it only for the simplicity** ::: Let's prepare a project with a base dependencies, so we can focus on the main goal of this tutorial. Create a new Rust project ```bash cargo new --bin indexer && cd indexer ``` Replace the content of the `Cargo.toml` file with this: ```toml title=Cargo.toml [package] name = "indexer" version = "0.1.0" edition = "2021" rust-version = "1.60.0" [dependencies] clap = { version = "3.1.6", features = ["derive"] } futures = "0.3.5" itertools = "0.9.0" tokio = { version = "1.1", features = ["sync", "time", "macros", "rt-multi-thread"] } tokio-stream = { version = "0.1" } tracing = "0.1.13" tracing-subscriber = "0.2.4" serde = { version = "1", features = ["derive"] } serde_json = "1.0.55" near-lake-framework = "0.3.0" ``` Replace the content of `src/main.rs` with this: ```rust use clap::{Parser, Subcommand}; use futures::StreamExt; use tracing_subscriber::EnvFilter; // TODO: StartOptions #[tokio::main] async fn main() -> Result<(), tokio::io::Error> { init_tracing(); let opts = Opts::parse(); // TODO: Config let stream = near_lake_framework::streamer(config); let mut handlers = tokio_stream::wrappers::ReceiverStream::new(stream) .map(handle_streamer_message) .buffer_unordered(1usize); while let Some(_handle_message) = handlers.next().await {} Ok(()) } async fn handle_streamer_message( streamer_message: near_lake_framework::near_indexer_primitives::StreamerMessage, ) { eprintln!( "{} / shards {}", streamer_message.block.header.height, streamer_message.shards.len() ); std::fs::write("last_indexed_block", streamer_message.block.header.height.to_string().as_bytes()).unwrap(); } fn init_tracing() { let mut env_filter = EnvFilter::new("near_lake_framework=info"); if let Ok(rust_log) = std::env::var("RUST_LOG") { if !rust_log.is_empty() { for directive in rust_log.split(',').filter_map(|s| match s.parse() { Ok(directive) => Some(directive), Err(err) => { eprintln!("Ignoring directive `{}`: {}", s, err); None } }) { env_filter = env_filter.add_directive(directive); } } } tracing_subscriber::fmt::Subscriber::builder() .with_env_filter(env_filter) .with_writer(std::io::stderr) .init(); } ``` This code is not going to build yet. Meanwhile let's have a quick look of what we've copy/pasted for now: - We have imported [`clap`](https://docs.rs/clap/latest/clap/) to set up what command line arguments we're going to accept - Also, we've important necessary stuff like `futures` and `tracing_subscriber` - `init_tracing` in the end of the file is a helper function that subscribes our application to the logs from `near-lake-framework` - An asynchronous `main` function with the indexer boilerplate code, but missing the `LakeConfig` creation part we're going to cover in the tutorial. - You can find a few `// TODO: ...` sections we've marked for you to find places to write the code from this tutorial. OK, all the preparations are done. Let's move on. --- ## Design the `StartOptions` So we want to be able to pass a command that defines the way our indexer should start. In this tutorial we'll be using `clap`. We need a structure that receives the chain id. This will allow us to use command: ```bash ./target/release/indexer mainnet ... ``` OR ```bash ./target/release/indexer testnet ... ``` Let's replace the `// TODO: StartOptions` in the `src/main.rs` with: ```rust title=src/main.rs #[derive(Parser, Debug, Clone)] #[clap(version = "0.1", author = "Near Inc. ")] struct Opts { #[clap(subcommand)] pub chain_id: ChainId, } #[derive(Subcommand, Debug, Clone)] enum ChainId { #[clap(subcommand)] Mainnet(StartOptions), #[clap(subcommand)] Testnet(StartOptions), } ``` Now we want to create a `StartOptions` structure that will allow us to tell our indexer where to start indexing from. The command should look like: ```bash ./target/release mainnet from-latest ``` Our variants are: - `from-block N`, where `N` is the block height to start from - `from-latest` to start from latest final block in the network - `from-interruption` to start from the block indexer was previously interrupted Let's replace the comment `// TODO: StartOptions` with the enum: ```rust title=src/main.rs #[derive(Subcommand, Debug, Clone)] pub(crate) enum StartOptions { FromBlock { height: u64 }, FromLatest, FromInterruption, } ``` Pretty simple and straightforward, agree? --- ## Creating a `LakeConfig` In order to create `LakeConfig` we're going to use a config builder [`LakeConfigBuilder`](https://docs.rs/near-lake-framework/0.3.0/near_lake_framework/struct.LakeConfigBuilder.html). Fotunately, we've imported it already. Let's instantiate a builder in place of `// TODO: Config` comment: ```rust title=src/main.src let mut lake_config_builder = near_lake_framework::LakeConfigBuilder::default(); ``` Notice that `lake_config_builder` is defined as mutable. Now we need to set the chain we are going to index by matching `ChainId` provided: ```rust title=src/main.src let mut lake_config_builder = near_lake_framework::LakeConfigBuilder::default(); match &opts.chain_id { ChainId::Mainnet(start_options) => { lake_config_builder = lake_config_builder .mainnet(); } ChainId::Testnet(start_options) => { lake_config_builder = lake_config_builder .testnet(); } } ``` As you can see, depending on the variant of the `ChainId` enum we modify the `lake_config_builder` with one of the shortcuts `mainnet()` or `testnet()`. The only parameter left to set is the most important for us in this tutorial `start_block_height` Normally, we just pass the block height number `u64` but we're implementing the start options here. --- ## Start options logic Let's create a separate function that will hold the logic of identification the `start_block_height` and call it `get_start_block_height`. **Just read the code, don't copy, it's not final approach yet** ### `FromBlock { height: u64 }` Let's start from implementation `from-block N` as the simplest one: ```rust title=src/main.rs async fn get_start_block_height(start_options: &StartOptions) -> u64 { match start_options { StartOptions::FromBlock { height } => height, } } ``` OK, it's simple enough, what's about other match arms for `StartOptions`: ```rust title=src/main.rs async fn get_start_block_height(start_options: &StartOptions) -> u64 { match start_options { StartOptions::FromBlock { height } => height, StartOptions::FromLatest => } } ``` Er, how should we get the latest block from the network? We should query the JSON RPC and get the final block, extract its height and call it a day.
### `FromLatest` In order to query the JSON RPC from within Rust code we need to use [`near-jsonrpc-client-rs` crate](https://github.com/near/near-jsonrpc-client-rs) You can find a [bunch of useful examples](https://github.com/near/near-jsonrpc-client-rs/tree/master/examples) in the corresponding folder of the project's repository on GitHub. Add it to `Cargo.toml` in the end: ```toml title=Cargo.toml near-jsonrpc-client = "0.3.0" ``` The code for getting the final block height would look like the following: ```rust use near_jsonrpc_client::{methods, JsonRpcClient}; use near_lake_framework::near_indexer_primitives::types::{BlockReference, Finality}; async fn final_block_height() -> u64 { let client = JsonRpcClient::connect("https://rpc.mainnet.near.org"); let request = methods::block::RpcBlockRequest { block_reference: BlockReference::Finality(Finality::Final), }; let latest_block = client.call(request).await.unwrap(); latest_block.header.height } ``` Nice and easy. Though, a hardcoded value of `"https://rpc.mainnet.near.org"` looks not so great. Especially when we want to support both networks. But we can handle it by passing the JSON RPC URL to the `get_start_block_function` like this: ```rust title=src/main.rs async fn get_start_block_height( start_options: &StartOptions, rpc_url: &str, ) -> u64 { ... } ... match &opts.chain_id { ChainId::Mainnet(start_options) => { lake_config_builder = lake_config_builder .mainnet() .start_block_height( get_start_block_height( start_options, "https://rpc.mainnet.near.org", ).await ); } ChainId::Testnet(start_options) => { lake_config_builder = lake_config_builder .testnet() .start_block_height( get_start_block_height( start_options, "https://rpc.testnet.near.org", ).await ) } } ``` Meh. It's ugly and why should we pass it everytime if it is required in only one case from three possible? Instead we can pass to the `get_start_block_height` function the entire `Opts`. ```rust title=src/main.rs async fn get_start_block_height(opts: &Opts) -> u64 { match opts.chain_id { ChainId::Mainnet(start_options) => { match start_options { StartOptions::FromBlock { height } => height, StartOptions::FromLatest => } } } } ``` At least we have everything we need. Though, it still looks ugly and will definitely involve code duplication. What we propose instead to is create `impl Opts` with a few useful methods to get JSON RPC URL and to get `StartOptions` instance. **Now you may proceed copying the code safely** Somewhere under the `StartOptions` definition add the following: ```rust title=src/main.rs impl Opts { pub fn rpc_url(&self) -> &str { match self.chain_id { ChainId::Mainnet(_) => "https://rpc.mainnet.near.org", ChainId::Testnet(_) => "https://rpc.testnet.near.org", } } pub fn start_options(&self) -> &StartOptions { match &self.chain_id { ChainId::Mainnet(args) | ChainId::Testnet(args) => args } } } ``` And now we can create our `get_start_block_height` function with the helper function that will query the final block `final_block_height` (we're going to reuse it, watch for the hands): ```rust title=src/main.rs async fn get_start_block_height(opts: &Opts) -> u64 { match opts.start_options() { StartOptions::FromBlock { height } => *height, StartOptions::FromLatest => final_block_height(opts.rpc_url()).await, // a placeholder StartOptions::FromInterruption => 0, } } async fn final_block_height(rpc_url: &str) -> u64 { let client = JsonRpcClient::connect(rpc_url); let request = methods::block::RpcBlockRequest { block_reference: BlockReference::Finality(Finality::Final), }; let latest_block = client.call(request).await.unwrap(); latest_block.header.height } ``` You may have noticed the `FromInterruption` and a comment about the placeholder. The reason we've made is to be able to build the application right now to test out that `FromLatest` works as expected.
### Testing `FromLatest` :::danger Credentials Please, ensure you've the credentials set up as described on the [Credentials](credentials.md) page. Otherwise you won't be able to get the code working. ::: Let's try to build and run our code ```bash cargo build --release ./target/release/indexer mainnet from-latest ``` Once the code is built you should see something like that in your terminal: ``` 65364116 / shards 4 65364117 / shards 4 65364118 / shards 4 65364119 / shards 4 65364120 / shards 4 ``` You can stop it by pressing `CTRL+C` And now we can move on to `FromInterruption`
### `FromInterruption` In order to let an indexer know at what block it was interrupted, the indexer needs to store the block height somewhere. And it should do it in the and of the `handle_message` function. In the boilerplate code you've copy/pasted in the beginning of this tutorial you can notice a line of code: ```rust std::fs::write("last_indexed_block", streamer_message.block.header.height.to_string().as_bytes()).unwrap(); ``` It saves the last indexed block height into a file `last_indexed_block` right near the indexer binary. In the real world indexer you'd probably go with some other storage, depending on the toolset you're using. But to show you the concept, we've decided to go with the easiest approach by saving it to the file. Now we need to implement the reading the value from the file. :::note If it is a first start of your indexer and you ask it to start from interruption it wouldn't be able to find `last_indexed_block` and would just fail. It's not the behavior we expect. That's why we assume you want it to start from interruption (if possible) or from the latest. ::: Let's finish up our `get_start_block_height` ```rust title=src/main.rs async fn get_start_block_height(opts: &Opts) -> u64 { match opts.start_options() { StartOptions::FromBlock { height } => *height, StartOptions::FromLatest => final_block_height(opts.rpc_url()).await, // a placeholder StartOptions::FromInterruption => { match &std::fs::read("last_indexed_block") { Ok(contents) => { String::from_utf8_lossy(contents).parse().unwrap() } Err(e) => { eprintln!("Cannot read last_indexed_block.\n{}\nStart indexer from latest final", e); latest_block_height(opts.rpc_url()).await } } }, } } ``` What we are doing here: - Trying to read the file `last_indexed_block` - If the `Result` is `Ok`, we are reading the `contents` and parsing it - If the `Result` is `Err` we print a message about the error and call `last_block_height` to get the final block from the network (the fallback we were talking earlier)
### Testing `FromInterruption` In order to ensure everything works as expected we will start index from the genesis to store the last indexed block. And then we will start it from interruption to ensure we're not starting from latest. Let's build and run from genesis. :::info Genesis Trick To start NEAR Lake Framework based indexer from the genesis block, you need to just specify the `start_block_height` as `0`. ::: ```bash cargo build --release ./target/release/indexer mainnet from-block 0 ``` You will see something like: ``` 9820210 / shards 1 9820214 / shards 1 9820216 / shards 1 9820219 / shards 1 9820221 / shards 1 9820226 / shards 1 9820228 / shards 1 9820230 / shards 1 9820231 / shards 1 9820232 / shards 1 9820233 / shards 1 9820235 / shards 1 9820236 / shards 1 9820237 / shards 1 9820238 / shards 1 ``` Stop it by pressing `CTRL+C` Memorize the last block height you see. In our example it is `9820238` Restart the indexer from interruption ```bash ./target/release/indexer mainnet from-interruption ``` You should see the indexer logs beginning from the block you've memorized. Perfect! It's all done. Now you can adjust the code you got in the result to your needs and use it in your indexers. --- ## Summary You've seen the way how you can empower your indexer with the starting options. As you can see there is nothing complex here. You can find the source code in the [`near-examples/lake-indexer-start-options`](https://github.com/near-examples/lake-indexer-start-options) --- # Source: https://docs.near.org/primitives/linkdrop/linkdrop.md --- id: linkdrop title: Using Linkdrops description: "Learn about linkdrops following NEP-452 standard - distribute assets and onboard users to Web3 apps through simple web links using access keys and the Keypom platform." --- Wanting to use Linkdrops in your dApp? Here you will find all the information you need to get started. :::tip The simplest way to create a linkdrop is by interacting with our [LinkDrop Generator](/toolbox) ::: --- ## AccessKeys In order to create any kind of drop, you need to first generate key pairs. You will need to create **one key per drop**. - The `linkdrop` contract will store the **`public`** part of the key. - You will give the `private` part of the key to the user you want to receive the drop. ```js import { KeyPair } from 'near-api-js'; const newKeyPair = KeyPair.fromRandom('ed25519'); newKeyPair.public_key = newKeyPair.publicKey.toString(); ``` ```bash near generate-key # Key pair with ed25519:33Vn9VtNEtWQPPd1f4jf5HzJ5weLcvGHU8oz7o5UnPqy public key for an account "1e5b1346bdb4fc5ccd465f6757a9082a84bcacfd396e7d80b0c726252fe8b3e8" ``` }>

Generate a new key on [Lantstool](https://app.lantstool.dev/)

![lantstool](/assets/docs/tools/lantstool-near_protocol-utils-key_generator.png)
--- ## $NEAR Drops To create a $NEAR drop you will ask the contract to create a drop (`create_drop`), passing the public part of the keys you generated, and how much you want to drop on each key use (`deposit_per_use`). The contract will create a drop and **return the numerical ID** that identifies it. ```js const KEYPOM_CONTRACT_ADDRESS = "v2.keypom.near"; const DROP_AMOUNT = "10000000000000000000000"; // 0.1 NEAR const { callFunction } = useNearWallet(); await callFunction({ contractId: KEYPOM_CONTRACT_ADDRESS, method: "create_drop", args: { public_keys: state.publicKeys, deposit_per_use: DROP_AMOUNT, }, deposit: "23000000000000000000000", // state.publicKeys.length * dropAmount + 3000000000000000000000 gas: "100000000000000", }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call v2.keypom.near create_drop '{"public_keys": , "deposit_per_use": "10000000000000000000000"}' --depositYocto 23000000000000000000000 --gas 100000000000000 --useAccount bob.near ``` }> :::info To claim the drop, you will need to send the user a [link with the private key](#building-drop-links) ::: :::tip The simplest way to create a linkdrop is by interacting with our [LinkDrop Generator](/toolbox) ::: --- ## NFT Drops To drop an existing NFT, you will (1) create a drop, and then (2) **transfer the NFT** to keypom. #### 1. Creating the Drop To create an NFT drop, you will call the `create_drop` method, now passing a `nft` argument, which will tell the linkdrop contract to wait for an NFT to be transferred. The contract will then create a drop and **return the numerical ID** that identifies it. ```js const KEYPOM_CONTRACT_ADDRESS = "v2.keypom.near"; const NFT_CONTRACT_ADDRESS = "nft.primitives.near"; const DROP_AMOUNT = "10000000000000000000000"; const { callFunction, accountId } = useNearWallet(); await callFunction({ contractId: KEYPOM_CONTRACT_ADDRESS, method: "create_drop", args: { public_keys: state.publicKeys, deposit_per_use: DROP_AMOUNT, nft: { // Who will be sending the NFTs to the Keypom contract sender_id: accountId, // NFT Contract Id that the tokens will come from contract_id: NFT_CONTRACT_ADDRESS, }, }, deposit: "23000000000000000000000", // state.publicKeys.length * dropAmount + 3000000000000000000000 gas: "100000000000000", }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call v2.keypom.near create_drop '{"public_keys": , "deposit_per_use": "10000000000000000000000", "nft": {"sender_id": "bob.near", "contract_id": "nft.primitives.near"}}' --depositYocto 23000000000000000000000 --gas 100000000000000 --useAccount bob.near ``` }> #### 2. Transferring the NFT Having the Drop ID, you now need to transfer the NFT to the linkdrop contract, specifying to which drop you want to add it. ```js const KEYPOM_CONTRACT_ADDRESS = "v2.keypom.near"; const NFT_CONTRACT_ADDRESS = "nft.primitives.near"; const NFT_TOKEN_ID = "1"; const { callFunction } = useNearWallet(); await callFunction({ contractId: NFT_CONTRACT_ADDRESS, method: "nft_transfer_call", args: { receiver_id: KEYPOM_CONTRACT_ADDRESS, token_id: NFT_TOKEN_ID, msg: dropId.toString() }, deposit: 1, gas: "100000000000000", }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call nft.primitives.near nft_transfer_call '{"receiver_id": "v2.keypom.near", "token_id": , "msg": }' --depositYocto 1 --gas 100000000000000 --useAccount bob.near ``` }> :::tip The `linkdrop` contract will validate that you are transferring the NFT to a drop that belongs to you ::: :::tip The simplest way to create a linkdrop is by interacting with our [LinkDrop Generator](/toolbox) ::: --- ## FT Drops The process to drop a Fungible Token is very similar to that of creating an [NFT drop](#nft-drops). You will first create the drop, and then fund it with FTs. #### 1.Creating a drop To create a FT drop you will call the `create_drop` method, now passing a `ftData` argument, which will tell the linkdrop contract to wait for a certain amount of FT to be transferred. The contract will then create a drop and **return the numerical ID** that identifies it. ```js const KEYPOM_CONTRACT_ADDRESS = "v2.keypom.near"; const FT_CONTRACT_ADDRESS = "ft.primitives.near"; const DROP_AMOUNT = "10000000000000000000000"; const { callFunction, accountId } = useNearWallet(); await callFunction({ contractId: KEYPOM_CONTRACT_ADDRESS, method: "create_drop", args: { public_keys: state.publicKeys, deposit_per_use: DROP_AMOUNT, ftData: { contractId: FT_CONTRACT_ADDRESS, senderId: accountId, // This balance per use is balance of human readable FTs per use. amount: "1" // Alternatively, you could use absoluteAmount, which is dependent on the decimals value of the FT // ex. if decimals of an ft = 8, then 1 FT token would be absoluteAmount = 100000000 }, }, deposit: "23000000000000000000000", // state.publicKeys.length * dropAmount + 3000000000000000000000 gas: "100000000000000", }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call v2.keypom.near create_drop '{"public_keys": , "deposit_per_use": "10000000000000000000000", "ftData": {"contractId": "ft.primitives.near","senderId": "bob.near", "amount": "1"}}}' --depositYocto 23000000000000000000000 --gas 100000000000000 --useAccount bob.near ``` }> #### 2. Transferring FT Having the Drop ID, you now need to transfer the fungible tokens to the linkdrop contract. :::note To transfer FTs to an account, you need to first [register](./ft#registering-a-user) the receiver account (e.g. the keypom contract) on the FT contract. ::: ```js const KEYPOM_CONTRACT_ADDRESS = "v2.keypom.near"; const FT_CONTRACT_ADDRESS = "ft.primitives.near"; const { callFunction } = useNearWallet(); await callFunction({ contractId: FT_CONTRACT_ADDRESS, method: "ft_transfer", args: { receiver_id: KEYPOM_CONTRACT_ADDRESS, amount: "1" }, deposit: "1", gas: "100000000000000" }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call ft.primitives.near ft_transfer '{"receiver_id": "v2.keypom.near", "amount": "1"}' --depositYocto 1 --gas 100000000000000 --useAccount bob.near ``` }> :::tip The simplest way to create a linkdrop is by interacting with our [LinkDrop Generator](/toolbox) ::: --- ## Function Call Drop Linkdrop contracts allow to create `function call` drops. These drops will execute one or more methods on a contract when the user claims the drop. :::tip Function call drops can be thought as the abstract version of other drops: you can create a drop that will mint an NFT, register a user in a DAO, or pay for a service. ::: ```js const KEYPOM_CONTRACT_ADDRESS = "v2.keypom.near"; const NFT_CONTRACT_ADDRESS = "nft.primitives.near"; const NFT_TOKEN_ID = "1"; const DROP_AMOUNT = "10000000000000000000000"; const { callFunction } = useNearWallet(); await callFunction({ contractId: KEYPOM_CONTRACT_ADDRESS, method: "create_drop", args: { public_keys: state.publicKeys, deposit_per_use: DROP_AMOUNT, fcData: { // 2D array of function calls. In this case, there is 1 function call to make for a key use // By default, if only one array of methods is present, this array of function calls will be used for all key uses methods: [ // Array of functions for Key use 1. [{ receiverId: NFT_CONTRACT_ADDRESS, methodName: "nft_mint", args: JSON.stringify({ // Change this token_id if it already exists -> check explorer transaction token_id: NFT_TOKEN_ID, metadata: { title: "My NFT drop", description: "", media: "", } }), accountIdField: "receiver_id", // Attached deposit for when the receiver makes this function call attachedDeposit: "10000000000000000000000" }] ] } }, deposit: "23000000000000000000000", // state.publicKeys.length * dropAmount + 3000000000000000000000 gas: "100000000000000", }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call v2.keypom.near create_drop '{"public_keys": , "deposit_per_use": "10000000000000000000000", "fcData": {"methods": [[{"receiverId": "nft.primitives.near","methodName": "nft_mint","args": {"token_id": "1", "metadata": {"title": "My NFT drop","description": "","media": ""}, "accountIdField": "receiver_id", "attachedDeposit": "10000000000000000000000"}]]}}' --depositYocto 23000000000000000000000 --gas 100000000000000 --useAccount bob.near ``` }> --- ## Building drop links To create a linkdrop link, simply append the private key to the `claim` page: ``` http://localhost:3001/claim/linkdrop?id=ed25519:5Ly2arHZ4niWBVyEuzpN3J8QQX1BrYfWsirGqdYR3JfqUDhJ3SRK7JeQfVsh4UL8Wn6uf8RzWE4RPHymkePywVVd ``` --- # Source: https://docs.near.org/primitives/liquid-staking/liquid-staking.md --- id: liquid-staking title: Using Liquid Staking description: "Learn about Liquid Staking on NEAR — a smart contract that issues a fungible token representing staked NEAR, enabling instant liquidity and validator diversification." --- Liquid staking is a modern alternative to regular staking on NEAR that solves the problem of delayed withdrawing. In regular staking, you can't immediately withdraw funds from a staking pool - first, you need to request an unstake and then wait for 4 epochs (roughly 24-28 hours) before your funds become available for withdrawal. And the liquid staking contract addresses this limitation by giving a flexible approach that keeps your assets liquid. :::tip Are you looking to delegate NEAR Tokens to a **liquid staking provider**? Check out the [Metapool](https://www.metapool.app/es/stake?token=near), [Rhea Finance](https://app.rhea.finance/stake) ::: --- ## Staking Pool The liquid staking contract implements a staking pool interface. From a developer’s perspective, interacting with a liquid staking contract feels almost identical to working with a regular staking pool. You can call familiar methods like `deposit_and_stake`, `unstake`, and `withdraw`, and they follow the same lifecycle - the only difference is that your stake is represented by a liquid token instead of being locked inside a single pool. Behind the scenes, the contract delegates tokens across multiple validators, usually selected from the top-performing ones on the network. These validators are actively maintained and closely monitored, which makes them far less likely to experience downtime or performance issues. And because your stake is distributed among many of them, the risk of losing rewards due to a single validator going offline becomes extremely low. It’s also worth noting that, since your stake is spread across several validators, the average reward rate will typically be slightly lower than if you delegated directly to a single, high-performing validator. But in return, you gain better security against validator outages and the ability to exchange your liquid token back to `NEAR` at any time without waiting through the unstaking delay. It’s a balanced trade-off between maximum yield and maximum flexibility. --- ## Using Liquid Staking Below are the most common commands you’ll use to interact with the staking flow, alongside with the contracts for major liquid staking providers on NEAR. | Provider | Testnet Account | Mainnet Account | |--------------|---------------------------|------------------------| | Metapool | `meta-v2.pool.testnet` | `meta-pool.near` | | Rhea Finance | | `lst.rhealab.near` | | Linear | `linear-protocol.testnet` | `linear-protocol.near` |
### Deposit and Stake Tokens To stake your `NEAR` and receive the liquid token, run the command: ```bash near contract call-function as-transaction deposit_and_stake json-args '{}' prepaid-gas '30.0 Tgas' attached-deposit '10 NEAR' ``` From that point, your tokens begin generating rewards through the underlying validators.
### Unstake When you feel ready, simply request unstake with the following command. You will need to wait standard delay of 4 epochs (24-28 hours) for funds to become available for withdrawal. ```bash near contract call-function as-transaction unstake_all json-args '{}' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` :::tip If you need your `NEAR` immediately, you don’t have to wait 4 epochs — you can simply swap your liquid tokens for `NEAR` on DEX like Rhea.finance. :::
### Withdraw As soon as 4 epochs have gone by, run the following command to withdraw `NEAR` tokens back. ```bash near contract call-function as-transaction withdraw_all json-args '{}' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` At this point, your liquid tokens will be burned, and you’ll receive the equivalent amount of `NEAR` based on the current exchange rate. --- # Source: https://docs.near.org/data-infrastructure/tutorials/listen-function-calls.md --- id: listen-function-calls title: "Tutorial: Simple Indexer" description: "This tutorial will guide you through building a simple indexer using the NEAR Lake Framework. The indexer will listen for FunctionCalls on a specific contract and log the details of each call." --- In this tutorial, we will build a simple indexer using the NEAR Lake Framework. The indexer will listen for FunctionCalls on a specific contract and log the details of each call. The full source code for the indexer is available in the [GitHub repository](https://github.com/near-examples/indexer-near-lake-framework?tab=readme-ov-file). :::info Using NEAR Lake Framework, we can subscribe to the stream of blocks from the NEAR Lake data source. The source of data are JSON files stored in an AWS S3 bucket by [NEAR Lake Indexer](https://github.com/aurora-is-near/near-lake-indexer). The NEAR Lake Framework takes care of downloading and parsing the data for users, but **the reader is paying the costs**. More details about technical limitations and **estimating costs** can be found [here](../near-lake-framework.md#comparison-with-near-indexer-framework). ::: --- ## Initialization Let's start by initializing the NEAR Lake Framework.
### AWS Credentials To access the data provided by [NEAR Lake](../lake-framework/near-lake) you need to provide valid AWS credentials in order to be charged by the AWS for the S3 usage. :::info AWS credentials Please note that using your own AWS Credentials is the only way to access the data provided by [NEAR Lake](../lake-framework/near-lake) ecosystem. ::: AWS default profile configuration with aws configure looks similar to the following: ``` ~/.aws/credentials ``` ``` [default] aws_access_key_id= aws_secret_access_key= ``` [AWS docs: Configuration and credential file settings](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html)
### Lake Configuration To initialize the NEAR Lake Framework, we need to provide the following basic settings: - The S3 bucket name: the bucket where the NEAR Lake data is stored. The value is `near-lake-data-testnet` for the testnet and `near-lake-data-mainnet` for the mainnet. - The S3 region: The AWS region where the S3 bucket is located. The default value is `eu-central-1`. - Start block height: The block height from which the indexer will start processing blocks. The Rust package provides a way to use the default configuration for testnet/mainnet and requires only to choose network and set the start block height which in the example we pass as command line argument. ``` // Parse the command line arguments let opts: Opts = Opts::parse(); // Configure the lake let lake_config = near_lake_framework::LakeConfigBuilder::default() .testnet() .start_block_height(opts.block_height) .build() .expect("Failed to build LakeConfig"); ``` In JavaScript/TypeScript, we will just create the configuration object manually. Block height is passed as a command line argument. ``` // Parse command line arguments const argv = yargs(hideBin(process.argv)).parse(); const blockHeight = argv.blockHeight; // Configure the lake const lakeConfig: types.LakeConfig = { s3BucketName: "near-lake-data-testnet", // "near-lake-data-mainnet" for the mainnet s3RegionName: "eu-central-1", startBlockHeight: blockHeight, }; ``` In Python, we will create the configuration object manually and then set `s3_bucket_name` and `s3_region_name` properties. Block height is passed as a command line argument. ``` # Parse command line arguments parser = argparse.ArgumentParser(description='NEAR Lake Framework Indexer') parser.add_argument('--accounts', type=str, help='Comma-separated list of accounts to filter') parser.add_argument('--block-height', type=int, help='Starting block height') args = parser.parse_args() start_height = args.block_height # Configure the lake config = LakeConfig( network=Network.TESTNET, aws_access_key_id=os.getenv("AWS_ACCESS_KEY_ID"), aws_secret_key=os.getenv("AWS_SECRET_ACCESS_KEY"), start_block_height=start_height ) config.s3_bucket_name = "near-lake-data-testnet" config.s3_region_name = "eu-central-1" ``` ## Running the Indexer To run the indexer, we need to create a function that will handle every message from the stream. In this function, we can access the block data and process it as needed. ``` /// The main listener function the will be reading the stream of blocks `StreamerMessage` async fn listen_blocks( mut stream: mpsc::Receiver, watching_list: Vec, ) { // This will be a map of correspondence between transactions and receipts let mut tx_receipt_ids = HashMap::::new(); // This will be a list of receipt ids we're following let mut wanted_receipt_ids = HashSet::::new(); // Handle the messages from the stream while let Some(streamer_message) = stream.recv().await { parse_message( &streamer_message, &watching_list, &mut tx_receipt_ids, &mut wanted_receipt_ids, ); } } ``` ``` // Start the stream (async () => { await startStream(lakeConfig, parseMessage); })(); ``` ``` async def listen_blocks(streamer_messages_queue, watching_accounts): # Dictionary to map the transaction receipt ids to the transaction hash we should track tx_receipt_ids = {} # List of receipt ids to track wanted_receipt_ids = [] while True: streamer_message = await streamer_messages_queue.get() parse_message(streamer_message, watching_accounts, tx_receipt_ids, wanted_receipt_ids) ``` ## Parsing the Block Data From the block data, we can access the transactions, their receipts and actions. In this example, we will look for FunctionCall actions on a specific contract and log the details of each call. ``` /// The method that will be called for each message from the stream fn parse_message( streamer_message: &near_indexer_primitives::StreamerMessage, watching_list: &[near_indexer_primitives::types::AccountId], tx_receipt_ids: &mut HashMap, wanted_receipt_ids: &mut HashSet, ) { eprintln!("Block height: {}", streamer_message.block.header.height); // Iterate over the shards in the block for shard in streamer_message.shards.clone() { let chunk = if let Some(chunk) = shard.chunk { chunk } else { continue; }; // Iterate over the transactions in the chunk for transaction in chunk.transactions { // Check if transaction receiver id is one of the list we are interested in if is_tx_receiver_watched(&transaction, &watching_list) { // Extract receipt_id transaction was converted into let converted_into_receipt_id = transaction .outcome .execution_outcome .outcome .receipt_ids .first() .expect("`receipt_ids` must contain one Receipt Id") .to_string(); // Add `converted_into_receipt_id` to the list of receipt ids we are interested in wanted_receipt_ids.insert(converted_into_receipt_id.clone()); // Add key value pair of transaction hash and in which receipt id it was converted for further lookup tx_receipt_ids.insert( converted_into_receipt_id, transaction.transaction.hash.to_string(), ); } } // Iterate over the execution outcomes in the shard for execution_outcome in shard.receipt_execution_outcomes { // Check if the receipt id is in the list of receipt ids to track if let Some(receipt_id) = wanted_receipt_ids.take(&execution_outcome.receipt.receipt_id.to_string()) { // Log the transaction hash, the receipt id and the status println!( "\nTransaction hash {:?} related to {} executed with status {:?}", tx_receipt_ids.get(receipt_id.as_str()), &execution_outcome.receipt.receiver_id, execution_outcome.execution_outcome.outcome.status ); if let near_indexer_primitives::views::ReceiptEnumView::Action { signer_id, .. } = &execution_outcome.receipt.receipt { // Log the signer id eprintln!("{}", signer_id); } if let near_indexer_primitives::views::ReceiptEnumView::Action { actions, .. } = execution_outcome.receipt.receipt { // Iterate over the actions in the receipt for action in actions.iter() { // If the action is a function call, log the decoded arguments if let near_indexer_primitives::views::ActionView::FunctionCall { args, .. } = action { if let Ok(args_json) = serde_json::from_slice::(&args) { eprintln!("{:#?}", args_json); } } } } // Remove the receipt id from the map of receipt ids to transaction hashes because we have processed it tx_receipt_ids.remove(receipt_id.as_str()); } } } } ``` ``` async function parseMessage( block: types.Block, /* context: types.LakeContext */ // In this case we don't use the context, but it is available if needed ): Promise { console.log(`Block height: ${block.header().height}`); // Iterate over the shards in the block for (const shard of block.streamerMessage.shards) { const chunk = shard.chunk; // Iterate over the transactions in the chunk for (const transaction of chunk.transactions) { // Check if transaction receiver id is one of the list we are interested in if (isTxReceiverWatched(transaction.transaction.receiverId, watchingAccounts)) { // Extract receipt_id transaction was converted into const receiptId = transaction.outcome.executionOutcome.outcome.receiptIds[0]; // Add receipt id to the list of receipt ids we are interested in wantedReceiptIds.push(receiptId); // Add key value pair of transaction hash and in which receipt id it was converted for further lookup txReceiptIds[receiptId] = transaction.transaction.hash; } } // Iterate over the execution outcomes in the shard for (const executionOutcome of shard.receiptExecutionOutcomes) { // Check if the receipt id is in the list of receipt ids to track if (wantedReceiptIds.includes(executionOutcome.receipt.receiptId)) { const receiptIdIdx = wantedReceiptIds.indexOf(executionOutcome.receipt.receiptId); // Remove the receipt id from the list of receipt ids because we have processed it wantedReceiptIds.splice(receiptIdIdx, 1); // Get the status of the execution outcome let status: string; if (typeof executionOutcome.executionOutcome.outcome.status === 'string') { status = 'Postponed'; } else if ('SuccessValue' in executionOutcome.executionOutcome.outcome.status) { status = 'SuccessValue'; } else if ('SuccessReceiptId' in executionOutcome.executionOutcome.outcome.status) { status = 'SuccessReceiptId'; } else if ('Failure' in executionOutcome.executionOutcome.outcome.status) { status = 'Failure'; } else { status = 'Unknown'; } // Log the transaction hash, the receipt id and the status console.log(`\nTransaction hash ${txReceiptIds[executionOutcome.receipt.receiptId]} related to ${executionOutcome.receipt.receiptId} executed with status \"${status}\"`); if ('Action' in executionOutcome.receipt.receipt) { // Log the signer id console.log(`${executionOutcome.receipt.receipt.Action.signerId}`); // Iterate over the actions in the receipt for (const action of executionOutcome.receipt.receipt.Action.actions) { // If the action is a function call, log the decoded arguments if (typeof action === 'object' && action !== null && 'FunctionCall' in action) { const decodedArgs = Buffer.from(action.FunctionCall.args, 'base64').toString('utf-8'); console.log(`${decodedArgs}`); } } } // Remove the receipt id from the map of receipt ids to transaction hashes because we have processed it delete txReceiptIds[executionOutcome.receipt.receiptId]; } } } } ``` ``` def parse_message(streamer_message, watching_accounts, tx_receipt_ids, wanted_receipt_ids): print(f"Block height: {streamer_message.block.header.height}") # Iterate over the shards in the block for shard in streamer_message.shards: chunk = shard.chunk # Iterate over the transactions in the chunk for tx in chunk.transactions: if tx.transaction.receiver_id in watching_accounts: # Extract receipt_id transaction was converted into receipt_id = tx.outcome.execution_outcome.outcome.receipt_ids[0] # Add receipt id to the list of receipt ids we are interested in wanted_receipt_ids.append(receipt_id) # Add key value pair of transaction hash and in which receipt id it was converted for further lookup tx_receipt_ids[receipt_id] = tx.transaction.hash # Iterate over the execution outcomes in the shard for execution_outcome in shard.receipt_execution_outcomes: # Check if the receipt id is in the list of receipt ids to track if execution_outcome.receipt.receipt_id in wanted_receipt_ids: # Remove the receipt id from the list of receipt ids because we have processed it wanted_receipt_ids.remove(execution_outcome.receipt.receipt_id) if isinstance(execution_outcome.execution_outcome.outcome.status, str): status = 'Postponed' elif 'SuccessValue' in execution_outcome.execution_outcome.outcome.status: status = 'SuccessValue' elif 'SuccessReceiptId' in execution_outcome.execution_outcome.outcome.status: status = 'SuccessReceiptId' elif 'Failure' in execution_outcome.execution_outcome.outcome.status: status = 'Failure' else: status = 'Unknown' # Log the transaction hash, the receipt id and the status print(f"Transaction hash {tx_receipt_ids[execution_outcome.receipt.receipt_id]} related to {execution_outcome.receipt.receipt_id} executed with status {status}") if ('Action' in execution_outcome.receipt.receipt): # Log the signer id print(execution_outcome.receipt.receipt['Action']['signer_id']) # Iterate over the actions in the receipt for action in execution_outcome.receipt.receipt['Action']['actions']: # If the action is a function call, log the decoded arguments if action['FunctionCall']: decoded_args = json.loads(base64.b64decode(action['FunctionCall']['args'])) print(decoded_args) ``` The example of logged data: ```bash Block height: 214692896 Transaction hash HQsRK16ABEQWtKpHKWMbPgUreXCD95ZpKw47YkHxGsEc related to 6QpDUkd5n2xJ6mTjkdzXDbvMFo5mEzANS1t4Hfr76SAY executed with status "SuccessValue" aha_6.testnet {"contract_id":"3vaopJ7aRoivvzZLngPQRBEd8VJr2zPLTxQfnRCoFgNX"} ``` :::note Versioning for this article At the time of this writing, this example works with the following versions: - near-lake-framework (Rust): `0.7.13` - @near-lake/framework (JS): `0.1.5` - near-lake-framework (Python): `0.1.3` - rustc: `1.86.0` - node: `22.18.0` - python: `3.13.5` ::: --- # Source: https://docs.near.org/data-infrastructure/tutorials/listen-to-realtime-events.md --- id: listen-to-realtime-events title: "Tutorial: Creating an Indexer" description: "This tutorial will guide you through building an indexer using the NEAR Indexer Framework. The indexer will listen for FunctionCalls on a specific contract and log the details of each call." --- In this tutorial, we will build an indexer using the NEAR Indexer Framework. The indexer will listen realtime blocks data from NEAR blockchain. To get our indexer up and running we will need two steps: 1. To [initialize](#initialization) the indexer 2. To [start it](#starting-the-indexer) The full source code for the indexer example is available in the [GitHub repository](https://github.com/near/nearcore/tree/master/tools/indexer/example). :::important Source code link is for `nearcore` repository, as the Indexer Framework is part of the `nearcore` codebase. We provide the link to `master` branch. If you want to use **the latest stable release version** you should check the [releases page](https://github.com/near/nearcore/releases) and checkout the corresponding tag. ::: :::danger NEAR Indexer Framework only works on **`Linux x86`**, it does **not** support Windows or MacOS ::: --- ## Prerequisites ### Install Rust ```bash curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ```
### Install developer tools ```bash apt update apt install -y git binutils-dev libcurl4-openssl-dev zlib1g-dev libdw-dev libiberty-dev cmake gcc g++ python docker.io protobuf-compiler libssl-dev pkg-config clang llvm cargo awscli ``` :::danger NEAR Indexer Framework only works on **`Linux x86`**, it does **not** support Windows or MacOS :::
### Clone nearcore repository ```bash git clone git@github.com:near/nearcore.git ``` --- ## Initialization In order for our indexer to process blocks it needs to join the NEAR network as a node. To do that, we need first to initialize it, which will download the blockchain `genesis` config, and create a `key` for our node to communicate with other nodes. Go to the `nearcore/tools/indexer/example` folder and build the indexer: ```bash cd nearcore/tools/indexer/example && cargo build --release ``` Then, run the following command to initialize the network configuration: ```bash cargo run --release -- --home-dir ~/.near/localnet init ``` ```bash cargo run --release -- --home-dir ~/.near/testnet init --chain-id testnet --download-config rpc --download-genesis ``` ```bash cargo run --release -- --home-dir ~/.near/mainnet init --chain-id mainnet --download-config rpc --download-genesis ``` Depending on the network we want to connect, the keys will be created in different folders (`~/.near/`). #### Config File A configuration file (`~/.near//config.json`) is created automatically, whoever, it is recommended to replace with one of the following ones, intended for RPC nodes: - [testnet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/testnet/rpc/config.json) - [mainnet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/mainnet/rpc/config.json) :::note Configuration Options See the [Custom Configuration](#custom-configuration) section below to learn more about further configuration options. ::: --- ## Starting the Indexer After we finish initializing the indexer, and configuring it, we can start it by running the following command: ```bash cargo run --release -- --home-dir ~/.near/localnet ``` ```bash cargo run --release -- --home-dir ~/.near/testnet run ``` ```bash cargo run --release -- --home-dir ~/.near/mainnet run ```
How it works - The command initializes the indexer's configuration: home directory, sync mode, streaming mode, finality, etc. - Creates a Tokio runtime on a dedicated thread. - Creates an instance of the Indexer using the provided configuration, starts it, and streams blocks to our handler. Within the handler (`listen_blocks` method), there is an infinite loop that parses block data for each new block received. ``` SubCommand::Run => { let indexer_config = near_indexer::IndexerConfig { home_dir, sync_mode: near_indexer::SyncModeEnum::FromInterruption, await_for_node_synced: near_indexer::AwaitForNodeSyncedEnum::WaitForFullSync, finality: near_primitives::types::Finality::Final, validate_genesis: true, }; let tokio_runtime = tokio::runtime::Builder::new_current_thread() .enable_all() .build() .expect("Failed to create Tokio runtime"); tokio_runtime.block_on(async move { let indexer = near_indexer::Indexer::new(indexer_config).await.expect("Indexer::new()"); let stream = indexer.streamer(); listen_blocks(stream).await; }); } SubCommand::Init(config) => near_indexer::indexer_init_configs(&home_dir, config.into())?, ```
#### Run into an Error? - If your indexer cannot find `boot nodes`, check the [boot nodes](#boot-nodes) section --- ## Parsing the Block Data Within the `listen_blocks` method, we can parse the block data as it flows from the stream. From the block data, we can access the transactions, their receipts, and actions. See the code below for an example of how to parse the block data: ``` async fn listen_blocks(mut stream: mpsc::Receiver) { while let Some(streamer_message) = stream.recv().await { // TODO: handle data as you need // Example of `StreamerMessage` with all the data (the data is synthetic) // // Note that `outcomes` for a given transaction won't be included into the same block. // Execution outcomes are included into the blocks after the transaction or receipt // are recorded on a chain; in most cases, it is the next block after the one that has // the transaction or receipt. // // StreamerMessage { // block: BlockView { // author: "test.near", // header: BlockHeaderView { // height: 63596, // epoch_id: `Bk7pvZWUTfHRRZtfgTDjnQ6y5cV8yG2h3orCqJvUbiym`, // next_epoch_id: `3JuBZ4Gz5Eauf7PzQegfqSEDyvws3eKJYPbfGHAYmeR5`, // hash: `5X37niQWWcihDGQjsvDMHYKLCurNJyQLxCeLgneDb8mk`, // prev_hash: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, // prev_state_root: `GkdxSBf4Kfq8V16N4Kqn3YdcThG1f5KG1KLBmXpMzP1k`, // chunk_receipts_root: `9ETNjrt6MkwTgSVMMbpukfxRshSD1avBUUa4R4NuqwHv`, // chunk_headers_root: `C7dVr9KdXYKt31yF2BkeAu115fpo79zYTqeU3FzqbFak`, // chunk_tx_root: `7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t`, // outcome_root: `7tkzFg8RHBmMw1ncRJZCCZAizgq4rwCftTKYLce8RU8t`, // chunks_included: 1, // challenges_root: `11111111111111111111111111111111`, // timestamp: 1618558205803345000, // timestamp_nanosec: 1618558205803345000, // random_value: `3cAa93XmoLaKAJQgWz3K7SiKwnA3uaxi8MGgLM78HTNS`, // validator_proposals: [], // chunk_mask: [ // true, // ], // gas_price: 1000000000, // rent_paid: 0, // validator_reward: 0, // total_supply: 2050206401403887985811862247311434, // challenges_result: [], // last_final_block: `DCkMmXYHqibzcMjgFjRXJP7eckAMLrA4ijggSApMNwKu`, // last_ds_final_block: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, // next_bp_hash: `4DJWnxRbUhRrsXK6EBkx4nFeXHKgJWqteDnJ7Hv4MZ6M`, // block_merkle_root: `Bvn5K89fJ3uPNsj3324Ls9TXAGUVteHPpfKwKqL1La6W`, // approvals: [ // Some( // ed25519:F816hgJod7nPfD2qQz5yhaKDMn1JXmvzj2iXegsJpsmPNnYYZpKYJXgyuVTVJ4TKQbcJ2Q3USCGZF6fX2TcwBBv, // ), // ], // signature: ed25519:239NbE4BuJaxneQA3AEsPrsGY7v3wBgaezbgg56HER69zPrBoc3a4fbyVWPXeoKE3LvgGma1g6pSHk9QHkmETCZY, // latest_protocol_version: 43, // }, // chunks: [ // ChunkHeaderView { // chunk_hash: `2M2oeNFBbUUnHfkU1UuBr8EKBCLMH9xr2vfsGRpyiBmA`, // prev_block_hash: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, // outcome_root: `11111111111111111111111111111111`, // prev_state_root: `3gZPPijaumgMRCvMuuZZM1Ab2LoHTSfYigMKwLqZ67m6`, // encoded_merkle_root: `79Bt7ivt9Qhp3c6dJYnueaTyPVweYxZRpQHASRRAiyuy`, // encoded_length: 8, // height_created: 63596, // height_included: 63596, // shard_id: 0, // gas_used: 0, // gas_limit: 1000000000000000, // rent_paid: 0, // validator_reward: 0, // balance_burnt: 0, // outgoing_receipts_root: `H4Rd6SGeEBTbxkitsCdzfu9xL9HtZ2eHoPCQXUeZ6bW4`, // tx_root: `11111111111111111111111111111111`, // validator_proposals: [], // signature: ed25519:2vWNayBzEoW5DRc7gTdhxdLbkKuK6ACQ78p3JGpKSAZZCarnLroeoALPAFwpr9ZNPxBqdVYh9QLBe7WHZebsS17Z, // }, // ], // }, // shards: [ // IndexerShard { // shard_id: 0, // chunk: Some( // IndexerChunkView { // author: "test.near", // header: ChunkHeaderView { // chunk_hash: `2M2oeNFBbUUnHfkU1UuBr8EKBCLMH9xr2vfsGRpyiBmA`, // prev_block_hash: `2vJNJca72pBiq2eETq2xvuoc6caKDaUkdRgtdefyutbA`, // outcome_root: `11111111111111111111111111111111`, // prev_state_root: `3gZPPijaumgMRCvMuuZZM1Ab2LoHTSfYigMKwLqZ67m6`, // encoded_merkle_root: `79Bt7ivt9Qhp3c6dJYnueaTyPVweYxZRpQHASRRAiyuy`, // encoded_length: 8, // height_created: 63596, // height_included: 0, // shard_id: 0, // gas_used: 0, // gas_limit: 1000000000000000, // rent_paid: 0, // validator_reward: 0, // balance_burnt: 0, // outgoing_receipts_root: `H4Rd6SGeEBTbxkitsCdzfu9xL9HtZ2eHoPCQXUeZ6bW4`, // tx_root: `11111111111111111111111111111111`, // validator_proposals: [], // signature: ed25519:2vWNayBzEoW5DRc7gTdhxdLbkKuK6ACQ78p3JGpKSAZZCarnLroeoALPAFwpr9ZNPxBqdVYh9QLBe7WHZebsS17Z, // }, // transactions: [ // IndexerTransactionWithOutcome { // transaction: SignedTransactionView { // signer_id: "test.near", // public_key: ed25519:8NA7mh6TAWzy2qz68bHp62QHTEQ6nJLfiYeKDRwEbU3X, // nonce: 1, // receiver_id: "some.test.near", // actions: [ // CreateAccount, // Transfer { // deposit: 40000000000000000000000000, // }, // AddKey { // public_key: ed25519:2syGhqwJ8ba2nUGmP9tkZn9m1DYZPYYobpufiERVnug8, // access_key: AccessKeyView { // nonce: 0, // permission: FullAccess, // }, // }, // ], // signature: ed25519:Qniuu7exnr6xbe6gKafV5vDhuwM1jt9Bn7sCTF6cHfPpYWVJ4Q6kq8RAxKSeLoxbCreVp1XzMMJmXt8YcUqmMYw, // hash: `8dNv9S8rAFwso9fLwfDQXmw5yv5zscDjQpta96pMF6Bi`, // }, // outcome: IndexerExecutionOutcomeWithReceipt { // execution_outcome: ExecutionOutcomeWithIdView { // proof: [], // block_hash: `G9v6Fsv94xaa7BRY2N5PFF5PJwT7ec6DPzQK73Yf3CZ6`, // id: `8dNv9S8rAFwso9fLwfDQXmw5yv5zscDjQpta96pMF6Bi`, // outcome: ExecutionOutcomeView { // logs: [], // receipt_ids: [ // `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, // ], // gas_burnt: 424555062500, // tokens_burnt: 424555062500000000000, // executor_id: "test.near", // status: SuccessReceiptId(CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz), // }, // }, // receipt: None, // }, // }, // ], // receipts: [ // ReceiptView { // predecessor_id: "test.near", // receiver_id: "some.test.near", // receipt_id: `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, // receipt: Action { // signer_id: "test.near", // signer_public_key: ed25519:8NA7mh6TAWzy2qz68bHp62QHTEQ6nJLfiYeKDRwEbU3X, // gas_price: 1030000000, // output_data_receivers: [], // input_data_ids: [], // actions: [ // CreateAccount, // Transfer { // deposit: 40000000000000000000000000, // }, // AddKey { // public_key: ed25519:2syGhqwJ8ba2nUGmP9tkZn9m1DYZPYYobpufiERVnug8, // access_key: AccessKeyView { // nonce: 0, // permission: FullAccess, // }, // }, // ], // }, // }, // ], // }, // ), // receipt_execution_outcomes: [ // IndexerExecutionOutcomeWithReceipt { // execution_outcome: ExecutionOutcomeWithIdView { // proof: [], // block_hash: `BXPB6DQGmBrjARvcgYwS8qKLkyto6dk9NfawGSmfjE9Q`, // id: `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, // outcome: ExecutionOutcomeView { // logs: [], // receipt_ids: [ // `8vJ1QWM4pffRDnW3c5CxFFV5cMx8wiqxsAqmZTitHvfh`, // ], // gas_burnt: 424555062500, // tokens_burnt: 424555062500000000000, // executor_id: "some.test.near", // status: SuccessValue(``), // }, // }, // receipt: ReceiptView { // predecessor_id: "test.near", // receiver_id: "some.test.near", // receipt_id: `CbWu7WYYbYbn3kThs5gcxANrxy7AKLcMcBLxLw8Zq1Fz`, // receipt: Action { // signer_id: "test.near", // signer_public_key: ed25519:8NA7mh6TAWzy2qz68bHp62QHTEQ6nJLfiYeKDRwEbU3X, // gas_price: 1030000000, // output_data_receivers: [], // input_data_ids: [], // actions: [ // CreateAccount, // Transfer { // deposit: 40000000000000000000000000, // }, // AddKey { // public_key: ed25519:2syGhqwJ8ba2nUGmP9tkZn9m1DYZPYYobpufiERVnug8, // access_key: AccessKeyView { // nonce: 0, // permission: FullAccess, // }, // }, // ], // }, // }, // }, // ], // }, // ], // state_changes: [ // StateChangeWithCauseView { // cause: ValidatorAccountsUpdate, // value: AccountUpdate { // account_id: "test.near", // account: AccountView { // amount: 1000000000000000000000000000000000, // locked: 50000000000000000000000000000000, // code_hash: `11111111111111111111111111111111`, // storage_usage: 182, // storage_paid_at: 0, // }, // }, // }, // ], // } tracing::info!( target: "indexer_example", height = %streamer_message.block.header.height, hash = ?streamer_message.block.header.hash, num_shards = %streamer_message.shards.len(), num_transactions = %streamer_message.shards.iter().map(|shard| if let Some(chunk) = &shard.chunk { chunk.transactions.len() } else { 0usize }).sum::(), num_receipts = %streamer_message.shards.iter().map(|shard| if let Some(chunk) = &shard.chunk { chunk.receipts.len() } else { 0usize }).sum::(), num_execution_outcomes = %streamer_message.shards.iter().map(|shard| shard.receipt_execution_outcomes.len()).sum::(), "block processed" ); } } ``` --- ## Custom Configuration By default, nearcore is configured to do as little work as possible while still operating on an up-to-date state. Indexers may have different requirements, so you might need to tweak the configuration based on yours. ### Shards/Accounts to Track We need to ensure that NEAR Indexer follows all the necessary shards, so by default the `"tracked_shards_config"` is set to `"AllShards"`. The most common tweak you might need to apply is listing to specific shards; to do that, lists all the shard UIDs you want to track in the `"tracked_shards_config"` section (`~/.near//config.json` file): ```json ... "tracked_shards_config": { "Shards": [ "s3.v3", "s4.v3" ] }, ... ``` Or, if you want to track specific accounts: ```json ... "tracked_shards_config": { "Accounts": [ "account_a", "account_b" ] }, ... ```
### Sync Mode You can choose Indexer Framework sync mode by setting what to stream: - LatestSynced - Real-time syncing, always taking the latest finalized block to stream - FromInterruption - Starts syncing from the block NEAR Indexer was interrupted last time - BlockHeight(u64) - Specific block height to start syncing from. ``` sync_mode: near_indexer::SyncModeEnum::FromInterruption, await_for_node_synced: near_indexer::AwaitForNodeSyncedEnum::WaitForFullSync, ```
### Streaming Mode You can choose Indexer Framework streaming mode by setting what to stream: - StreamWhileSyncing - Stream while node is syncing - WaitForFullSync - Don't stream until the node is fully synced ``` await_for_node_synced: near_indexer::AwaitForNodeSyncedEnum::WaitForFullSync, finality: near_primitives::types::Finality::Final, ```
### Finality You can choose finality level at which blocks are streamed: - None - `optimistic`, a block that (though unlikely) might be skipped - DoomSlug - `near-final`, a block that is irreversible, unless at least one block producer is slashed - Final - `final`, the block is final and irreversible. ``` finality: near_primitives::types::Finality::Final, validate_genesis: true, ```
### Boot Nodes If your node can't find any peers to connect to, you can manually specify some boot nodes in the `config.json` file. You can get a list of active peers for your network by running the following command: ```bash curl -X POST https://rpc.testnet.near.org \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "network_info", "params": [], "id": "dontcare" }' | \ jq '.result.active_peers as $list1 | .result.known_producers as $list2 | $list1[] as $active_peer | $list2[] | select(.peer_id == $active_peer.id) | "\(.peer_id)@\($active_peer.addr)"' |\ awk 'NR>2 {print ","} length($0) {print p} {p=$0}' ORS="" | sed 's/"//g' ``` ```bash curl -X POST https://rpc.mainnet.near.org \ -H "Content-Type: application/json" \ -d '{ "jsonrpc": "2.0", "method": "network_info", "params": [], "id": "dontcare" }' | \ jq '.result.active_peers as $list1 | .result.known_producers as $list2 | $list1[] as $active_peer | $list2[] | select(.peer_id == $active_peer.id) | "\(.peer_id)@\($active_peer.addr)"' |\ awk 'NR>2 {print ","} length($0) {print p} {p=$0}' ORS="" | sed 's/"//g' ``` And then add the output to the `boot_nodes` section of your `config.json` file as a string: ```json ... "network": { "addr": "0.0.0.0:24567", "boot_nodes": "ed25519:8oVENgBp6zJfnwXFe...", ... }, ... ```
### Historical Data Indexer Framework also exposes access to the internal APIs (see Indexer::client_actors method), so you can fetch data about any block, transaction, etc, yet by default, nearcore is configured to remove old data (garbage collection), so querying the data that was observed a few epochs before may return an error saying that the data is not found. If you only need blocks streaming, you don't need this tweak, but if you need access to the historical data right from your Indexer, consider updating "archive" setting in config.json to true: ```json ... "archive": true, ... ``` --- ## Using NEAR Indexer in your Project You can also use NEAR Indexer Framework as a dependency in your own Rust project. To do that, add the following to your `Cargo.toml` file (replace `2.8.0` with the latest stable release version): ```toml [dependencies] near-indexer = { git = "https://github.com/near/nearcore", tag = "2.9.1" } near-indexer-primitives = { git = "https://github.com/near/nearcore", tag = "2.9.1" } near-config-utils = { git = "https://github.com/near/nearcore", tag = "2.9.1" } near-o11y = { git = "https://github.com/near/nearcore", tag = "2.9.1" } near-primitives = { git = "https://github.com/near/nearcore", tag = "2.9.1" } ``` --- # Source: https://docs.near.org/smart-contracts/release/lock.md --- id: lock title: Locking Accounts description: "Learn how to lock NEAR smart contracts to prevent unauthorized modifications and ensure contract immutability when needed." --- In NEAR, you can lock an account by removing all its [full access keys](/tools/near-cli#delete-key). This is useful to prevent any further transactions from being executed in the account's name, effectively locking it. Removing all [full access keys](/tools/near-cli#delete-key) from an account will effectively **lock it**. When an account is locked nobody can perform transactions in the account's name (e.g. update the code or transfer money). #### How to Lock an Account ```bash near list-keys # result: [access_key: {"nonce": ..., "public_key": ''}] near delete-key '' ``` #### Why Locking an Account Locking an account brings more reassurance to end-users, since they know no external actor will be able to manipulate the account's contract or balance. :::tip Upgrading Locked Contracts While no external actor can update the contract, the contract **can still upgrade itself**, see [this article](upgrade.md#programmatic-update) for details ::: --- # Source: https://docs.near.org/primitives/lockup/lockup.md --- id: lockup title: Lockup Contracts description: "Learn about Lockup contracts on NEAR – smart contracts that escrow tokens with time-based release schedules, supporting lockups, vesting, staking, and termination by foundation." --- Looking to manage token vesting and lockup schedules? NEAR's Lockup contracts provide a robust solution for escrowing tokens with time-based release mechanisms. --- ## Deploying a Lockup Contract The simplest way to deploy a new lockup contract is to use the existing [global contract](../../smart-contracts/global-contracts.md) already published on NEAR. It is important to understand that the **total amount of tokens to be locked is determined by the balance of the account at the moment of deployment**. Whatever tokens are in the account once you deploy the contract will become locked under the schedule you define. This means that you must always **first create and fund the lockup account** with the intended amount of tokens, and only after that deploy the contract. ```bash # Create and fund the lockup account near create-account --useAccount --initialBalance # Deploy the lockup contract near contract deploy use-global-hash CAvU5MYQ4xk1SjFvbnQDQUj6qehuW5YhU3FXA6GMddCx with-init-call new json-args '{ "owner_account_id": "employee.near", "lockup_duration": "0", "lockup_timestamp": "1535760000000000000", "release_duration": "126230400000000000", "transfers_information": { "TransfersEnabled": { "transfers_timestamp": "1602614338293769340" } }, "vesting_schedule": { "VestingSchedule": { "start_timestamp": "1535760000000000000", "cliff_timestamp": "1567296000000000000", "end_timestamp": "1661990400000000000" } }, "staking_pool_whitelist_account_id": "staking-pool-whitelist", "foundation_account_id": "foundation.near" }' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` :::tip - To use lockup only: omit the `vesting_schedule` property. - To use vesting only: set `lockup_timestamp` and `release_duration` to 0 and provide only the `vesting_schedule`. ::: ### Checking Balance To see the owner’s balance, run the following command. The returned amount includes both the total amount of tokens in the contract and any staking rewards that are available for withdrawal. ```bash near contract call-function as-read-only get_owners_balance json-args '{}' ``` To see the liquid balance, run the following command. This will return the amount that is available for withdrawal, including unlocked tokens and staking rewards. ```bash near contract call-function as-read-only get_liquid_owners_balance json-args '{}' ``` ### Withdraw Tokens To transfer unlocked tokens to your account, please run the following command. ```bash near contract call-function as-transaction transfer json-args '{ "amount": "10000000000000000000000000", "receiver_id":"owner.near" }' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` :::warning Requesting to withdraw amount that is greater than liquid won't go through ::: ### Withdraw Tokens Once Fully Unlocked Once all tokens are fully vested and unlocked, you can convert your lockup account into a regular NEAR account by adding a full access key. This allows you to manage the account directly and withdraw the remaining tokens by leveraging the power of advanced technique, which includes deleting it account completely and specifying your account as beneficiary. Generate a new key pair and run the following command to add it as full access key to account, once the vesting/locking schedule is finished. ```bash near contract call-function as-transaction add_full_access_key json-args '{ "new_public_key": "ed25519:8W9CiyPPehz2GRW8AYho9nx1z1GLdeZQCyn2wqYgJjiG" }' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` :::warning Keep in mind that you never should use the key from the command above. Generate your own key pair. ::: Once the key is added, you’ll be able to delete it and transfer remaining funds to your account (replace `owner.near` with your address) with the following command. ```bash near account delete-account beneficiary owner.near ``` :::danger Make sure all staked tokens are fully withdrawn before attempting to delete the account. If any tokens remain delegated to a staking pool, they will be lost once the account is deleted. ::: --- ## Staking The Lockup contract allows the owner to delegate tokens to a whitelisted staking pool, and it lets the owner to earn additional rewards while their base tokens remain locked. Let’s walk through each step slowly, in the order you will typically execute the commands. ### Select Staking Pool Choose a validator from the whitelist configured at initialization, then set it on the lockup contract. ```bash near contract call-function as-transaction select_staking_pool json-args '{ "staking_pool_account_id": "pool.testnet" }' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` ### Deposit and Stake Tokens Stake part of tokens, available on the lockup contract. The amount must be passed over in yoctoNEAR (1 NEAR = 10^24 yoctoNEAR). ```bash near contract call-function as-transaction deposit_and_stake json-args '{ "amount": "1000000000000000000000000000" }' prepaid-gas '125.0 Tgas' attached-deposit '0 NEAR' ``` ### Refresh Staking Pool Balance The rewards from staking pool won't be automatically reflected in the contract. This command synchronizes the contract’s internal balance with the staking pool. ```bash near contract call-function as-transaction refresh_staking_pool_balance json-args '{}' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' ``` Once that's done, notice that [liquid balance](#checking-balance) has changed. ### Unstake When you feel ready, simply request unstake with the following command. You will need to wait standard delay of 4 epochs for funds to become withdrawable from the pool. ```bash near contract call unstake_all json-args '{}' prepaid-gas '125.0 Tgas' attached-deposit '0 NEAR' ``` ### Withdraw As soon as 4 epochs have gone by, run the following command to move the funds from the staking pool back to the contract, including earned rewards. ```bash near contract call withdraw_all_from_staking_pool json-args '{}' prepaid-gas '175.0 Tgas' attached-deposit '0 NEAR' ``` Once that's done, earned rewards become part of liquid balance and [can be withdrawn to your account immediately](#withdraw-tokens). --- ## Termination (for Foundations) The lockup can be terminated early by a `foundation_account_id`, if it was specified during the initialization. Before the cliff, the entire allocation is refunded to the foundation. After the cliff, only the unvested portion is refunded, while the vested part remains with the owner. During termination, some owner actions are temporarily paused until the process completes. ### Initiate Start the termination process. After initiation, the contract may restrict owner actions until termination is finalized. ```bash near contract call-function as-transaction terminate_vesting json-args '{}' prepaid-gas '25.0 Tgas' attached-deposit '0 NEAR' ``` ### Check Status The status tells you which step is safe to run next and prevents failed calls. For example, a deficit status means there're some tokens left on the staking pool and it [requires additional actions](#resolve-deficit-if-staked) from you. And, once the contract reports "Ready To Withdraw" status, the foundation can proceed to withdraw. ```bash near contract call-function as-read-only get_termination_status json-args '{}' ``` ### Resolve Deficit (if staked) If some tokens are staked and the contract shows a deficit (not enough liquid balance to refund the unvested portion), prepare the funds. ```bash near contract call-function as-transaction termination_prepare_to_withdraw json-args '{}' prepaid-gas '175.0 Tgas' attached-deposit '0 NEAR' ``` :::warning You need to call it twice — once to start unstaking, and again after the 4 epoch delay to withdraw your funds. ::: ### Withdraw Unvested Once the status is ready, withdraw the unvested portion to the foundation account. ```bash near contract call-function as-transaction termination_withdraw json-args '{ "receiver_id": "foundation.near" }' prepaid-gas '75.0 Tgas' attached-deposit '0 NEAR' ``` --- ## Additional Resources For more information, explore the official resources below: - [Lockup Contract Repository](https://github.com/near/core-contracts/tree/master/lockup) - [NEAR CLI](https://github.com/near/near-cli-rs) --- # Source: https://docs.near.org/api/rpc/maintenance-windows.md --- id: maintenance-windows title: Maintenance Windows description: "Query future maintenance windows for validators in the current epoch using the NEAR RPC API." hide_table_of_contents: true --- The RPC API enables you to query future maintenance windows for a specific validator in the current epoch. These maintenance windows are block height ranges where the validator does not need to produce blocks or chunks. ## Quick Reference | Method | Purpose | Parameters | |--------|---------|------------| | [`maintenance_windows`](#maintenance-windows) | Get validator maintenance windows | `account_id` | --- ## Maintenance windows {#maintenance-windows} The maintenance windows for a specific validator are future block height ranges in current epoch, in which the validator does not need produce block or chunk. If the provided account is not a validator, then it will return the range from now to the end of the epoch. - **method**: `maintenance_windows` - **params**: - `account_id`: The validator account ID to query ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "maintenance_windows", "params": { "account_id": "node0" } } ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=maintenance_windows \ params:='{ "account_id": "node0" }' ``` }>
Example response: ```json { "jsonrpc": "2.0", "id": "dontcare", "result": [ [1028, 1031], [1034, 1038] ] } ```

The result will be a list of future maintenance windows in current epoch. For example a window `[1028, 1031]` includes block heights 1028, 1029 and 1030.

Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Error Handling ### Common Error Types | Error Code | Description | Solution | |------------|-------------|----------| | `UnknownAccount` | Account does not exist | Verify the account ID is correct and exists | | `InvalidAccount` | Invalid account format | Use valid account ID format (e.g., `account.near`) | | `InternalError` | Server internal error | Retry the request or try a different RPC endpoint | | `RequestTimedOut` | Request timed out | Reduce request complexity or retry with timeout | ### Response Format Successful responses return an array of maintenance window ranges: - Each window is represented as `[start_height, end_height]` - Ranges are inclusive of start height, exclusive of end height - Empty array indicates no maintenance windows for the validator --- ## Best Practices - **Cache results**: Maintenance windows don't change frequently during an epoch - **Monitor epoch changes**: Query again when a new epoch starts --- # Source: https://docs.near.org/chain-abstraction/meta-transactions-relayer.md --- id: meta-transactions-relayer title: Building a Meta Transaction Relayer sidebar_label: Building a Relayer description: "Learn how to build a meta transaction relayer that allows users to transact on NEAR without paying gas fees while maintaining transaction security through signed delegates." --- Relayers serve to delegate gas fees to a web service, allowing users to transact on NEAR without the need to acquire the token themselves while still retaining the security of signing their own transactions. This guide will lead you through the components necessary to construct a relayer capable of handling meta transactions. :::tip If you're already acquainted with the technology and you just want to run your own Relayer, you can fast track to a complete [Rust Relayer server](#rust-relayer-server) open-source implementation. ::: ## How it works A basic relayer consists of a web server housing a funded NEAR account. This account receives an encoded signed transaction, which can subsequently be decoded into a `SignedDelegate` format and transmitted on-chain. The client can then generate a `SignedDelegateAction` (a signed message that hasn't yet been sent), encode it, and transmit it to this server, where it will be relayed onto the blockchain. ![relayer-overview-technical](/assets/docs/welcome-pages/relayer-overview-technical.png) ## Relayer (server) Here's a simple express endpoint that deserializes the body, instantiates the relayer account and then sends the transaction. ``` app.post('/', async (req: Request, res: Response) => { const serializedTxs: Buffer[] = req.body; const relayerAccount: Account = await getAccount( NETWORK_ID, RELAYER_ID, RELAYER_PRIVATE_KEY, ); const outcomes = await Promise.all( serializedTxs.map(async (serializedTx) => { const deserializedTx: SignedDelegate = deserialize( SCHEMA.SignedDelegate, ``` You can easily get the account object used to send the transactions from its private key using this snippet ``` export async function getAccount(network: string, accountId: string, privateKey: string): Promise { const keyStore: InMemoryKeyStore = new InMemoryKeyStore(); await keyStore.setKey(network, accountId, KeyPair.fromString(privateKey as KeyPairString)); const config = { networkId: network, keyStore, nodeUrl: `https://rpc.${network}.near.org`, }; const near = await connect(config); return near.account(accountId); } ``` :::info The code in the example only works from the following versions onwards ``` "near-api-js": "3.0.4" "@near-js/transactions": "1.1.2", "@near-js/accounts": "1.0.4" ``` ::: `@near-relay` simplifies meta transactions making it easier to get started for a beginner. To start, call the relay method inside an endpoint to automatically deserialize the transaction and send it with the account defined in the environment variables. ``` app.post('/', async (req: any, res: any) => { const body = req.body; const results = await relay(body) res.json({ message: 'Relayed', data: results }); }); ``` If you're interested in relaying account creation as well, it's quite straightforward. Simply create another endpoint and directly call the createAccount method with the accountId and publicKey. These parameters are automatically included in the body when using the corresponding client library. ``` app.post('/create-account', async (req: any, res: any) => { const body = req.body; const result = await createAccount(body.accountId, body.publicKey) res.json({ message: 'Relayed', data: result }); }); ``` ## Client In this method we are creating an arbitrary smart contract call, instantiating an account and using it to sign but not send the transaction. We can then serialize it and send it to the relayer where it will be delegated via the previously created endpoint. ``` async function sendRelay() { const action = actionCreators.functionCall( 'set_greeting', { greeting: 'hello', }, 3000000000000n, ); const account = await getAccount(NETWORK_ID, CLIENT_ID, CLIENT_PRIVATE_KEY); const signedDelegate = await account.signedDelegate({ actions: [action], blockHeightTtl: 120, receiverId: CONTRACT_ID, }); const res = await fetch(SERVER_URL, { method: 'POST', mode: 'cors', body: JSON.stringify([Array.from(encodeSignedDelegate(signedDelegate))]), headers: new Headers({ 'Content-Type': 'application/json' }), ``` As mentioned in the above note in order to be able to relay on the client side it's necessary to have access to signing transactions directly on the client. Luckily leveraging the near biometric library it's possible to do so in a non custodial way. By calling this method and passing in the URL for the account creation endpoint (mentioned in the server section) as well as the `accountId` everything is handled under the hood to successfully create an account. ``` const [accountId, setAccountId] = useState(""); const [password, setPassword] = useState(""); const [createReceipt, setCreateReceipt] = useState(); const [relayReceipt, setRelayReceipt] = useState(); const [error, setError] = useState(); const [createMethod, setCreateMethod] = useState<"passkey" | "password" | "keypair">("passkey"); const [isCreating, setIsCreating] = useState(false); const [isRelaying, setIsRelaying] = useState(false); ``` On the client side, you just need to create an `Action` and pass it into the `relayTransaction` method along with the URL of the relayer endpoint discussed in the server section and the id of the `receiverId`. ``` const handleCreateAccount = async () => { try { setIsCreating(true); let receipt; switch(createMethod) { case "passkey": receipt = await createAccount( CREATE_ACCOUNT_URL, accountId, { usePasskey: true } ); ```
Relaying with wallets At the moment, wallet selector standard doesn't support signing transactions without immediately sending them. This functionality is essential for routing transactions to a relayer. Therefore, to smoothly integrate relaying on the client side, it's necessary to be able to sign transactions without relying on wallets. Progress is being made to make this possible in the future.
### High volume parallel processing When running a relayer that handles a large number of transactions, you will quickly run into a `nonce` collision problem. At the protocol level, transactions have a unique number that identifies them (nonce) that helps to mitigate reply attacks. Each key on an account has its own nonce, and the nonce is expected to increase with each signature the key creates. When multiple transactions are sent from the same access key simultaneously, their nonces might collide. Imagine the relayer creates 3 transactions `Tx1`, `Tx2`, `Tx3` and send them in very short distance from each other, and lets assume that `Tx3` has the largest nonce. If `Tx3` ends up being processed before `Tx1` (because of network delays, or a node picks `Tx3` first), then `Tx3` will execute, but `Tx1` and `Tx2` will fail, because they have smaller nonce! One way to mitigate this is to sign each transaction with a different key. Adding multiple full access keys to the NEAR account used for relaying (up to 20 keys can make a significant difference) will help.
Adding keys ```js const keyPair = nearAPI.KeyPair.fromRandom("ed25519"); const receipt = await account.addKey(keyPair.getPublicKey().toString()) ``` After saving these keys, its possible to rotate the private keys randomly when instantiating accounts before relaying ensuring you won't create a nonce collision.
### Gating the relayer In most production applications it's expected that you want to be able to gate the relayer to only be used in certain cases. This can be easily accomplished by specifying constraints inside the `SignedDelegate.delegateAction` object. ```typescript export declare class DelegateAction extends Assignable { senderId: string; receiverId: string; actions: Array; nonce: BN; maxBlockHeight: BN; publicKey: PublicKey; } ``` You can, for example, gate by some particular user or contract: ```typescript const serializedTx: Buffer = req.body; const deserializedTx: SignedDelegate = deserialize(SCHEMA.SignedDelegate, Buffer.from(serializedTx)) as SignedDelegate; const relayerAccount: Account = await getAccount(NETWORK_ID, RELAYER_ID, RELAYER_PRIVATE_KEY); const delegateAction = deserializedTx?.delegateAction if(delegateAction.senderId == 'someUserId' || delegateAction.receiverId == 'someContractId' ){ const receipt = await relayerAccount.signAndSendTransaction({ actions: [actionCreators.signedDelegate(deserializedTx)], receiverId: deserializedTx.delegateAction.senderId }); } ``` Other examples could be looking into the actions and seeing if there is deposit or gas and limiting them, gating by particular smart contract methods or even args. You can decode the args using: ``` JSON.parse(Buffer.from(args_base64 || "", "base64").toString()) ``` --- ## Rust Relayer Server The open-source Rust [reference implementation of a Relayer server](https://github.com/near/pagoda-relayer-rs/) offers the following features: :::info Features can be combined as needed. Use of one feature does not preclude the use of any other feature unless specified. ::: 1. Sign and send Meta Transactions to the RPC to cover the gas costs of end users while allowing them to maintain custody of their funds and approve transactions (`/relay`, `/send_meta_tx`, `/send_meta_tx_async`, `/send_meta_tx_nopoll`) 2. Sign Meta Transactions returning a Signed Meta Transaction to be sent to the RPC later - (`/sign_meta_tx`, `/sign_meta_tx_no_filter`) 3. Only pay for users interacting with certain contracts by whitelisting contracts addresses (`whitelisted_contracts` in `config.toml`) 4. Specify gas cost allowances for all accounts (`/update_all_allowances`) or on a per-user account basis (`/create_account_atomic`, `/register_account`, `/update_allowance`) and keep track of allowances (`/get_allowance`) 5. Specify the accounts for which the relayer will cover gas fees (`whitelisted_delegate_action_receiver_ids` in `config.toml`) 6. Only allow users to register if they have a unique Oauth Token (`/create_account_atomic`, `/register_account`) 7. Relayer Key Rotation: `keys_filenames` in `config.toml` 8. Integrate with [FastAuth SDK](fastauth-sdk.md) 9. Mix and Match configuration options :::tip Check the [Use cases section](#use-cases) for example configuration files corresponding to different usage scenarios. ::: ### Basic Setup You can follow these steps to set up your local Relayer server development environment: 1. [Install Rust for NEAR Development](../smart-contracts/quickstart.md#prerequisites) 2. If you don't have a NEAR account, [create one](../protocol/account-model.md) 3. With the account from step 2, create a JSON file in this directory in the format ```js [{"account_id":"example.testnet","public_key":"ed25519:98GtfFzez3opomVpwa7i4m3nptHtc7Ha514XHMWszLtQ","private_key":"ed25519:YWuyKVQHE3rJQYRC3pRGV56o1qEtA1PnMYPDEtroc5kX4A4mWrJwF7XkzGe7JWNMABbtY4XFDBJEzgLyfPkwpzC"}] ``` using a [Full Access Key](../protocol/access-keys.md#full-access-keys) from an account that has enough NEAR to cover the gas costs of transactions your server will be relaying. Usually, this will be a copy of the json file found in the `.near-credentials` directory. 4. Update values in `config.toml` 5. Open up the `port` from `config.toml` in your machine's network settings 6. Run the server using `cargo run`. > **(OPTIONAL)** To run with logs (tracing) enabled run `RUST_LOG=tower_http=debug cargo run` :::info Optional setup If you're integrating with [FastAuth](fastauth-sdk.md) make sure to enable feature flags: ``` cargo build --features fastauth_features,shared_storage ``` If you're using shared storage, make sure to enable feature flags: ``` cargo build --features shared_storage ``` ::: ### Redis Setup :::tip This is only needed if you intend to use whitelisting, allowances, and OAuth functionality. ::: 1. [Install Redis](https://redis.io/docs/latest/get-started/). > Steps 2 & 3 assume Redis was installed on machine instead of a Docker setup. If you're connecting to a Redis instance running in GCP, follow the above steps to connect to a VM that will forward requests from your local relayer server to [Redis running in GCP](https://cloud.google.com/memorystore/assets/docs/redis/connect-redis-instance#connecting_from_a_local_machine_with_port_forwarding) 2. Run `redis-server --bind 127.0.0.1 --port 6379` - make sure the port matches the `redis_url` in the `config.toml`. 3. Run `redis-cli -h 127.0.0.1 -p 6379` ### Advanced setup - [Multiple Key Generation](https://github.com/near/pagoda-relayer-rs/tree/main?tab=readme-ov-file#multiple-key-generation---optional-but-recommended-for-high-throughput-to-prevent-nonce-race-conditions): this is optional, but recommended for high throughput to prevent nonce race conditions. Check - [Docker Deployment](https://github.com/near/pagoda-relayer-rs/tree/main?tab=readme-ov-file#docker-deployment): instructions to deploy with Docker - [Cloud Deployment](https://github.com/near/pagoda-relayer-rs/tree/main?tab=readme-ov-file#cloud-deployment): instructions to deploy on Cloud providers ### API Specifications You can find the complete Relayer server API specification on the [GitHub repository](https://github.com/near/pagoda-relayer-rs/tree/main?tab=readme-ov-file#api-spec-). ### Use cases The [examples folder](https://github.com/near/pagoda-relayer-rs/tree/main/examples) on the GitHub repository contains example configuration files corresponding to different use cases. :::info These files are for reference only and you should update the `config.toml` values before using it on your development environment. ::: #### No filters This is a config for a relayer that covers gas for all user transactions to all contracts with no filters. To prevent abuse, this should only be used if there's only a secure backend calling the relayer - [`no_filters.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/no_filters.toml) #### Basic whitelist This is a configuration for a basic relayer that covers gas for user transactions to interact with a whitelisted set of contracts - [`basic_whitelist.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/basic_whitelist.toml) #### Redis This is a configuration for a relayer that covers gas for user transactions up to a allowance specified in Redis to interact with a whitelisted set of contracts. - Allowances are on a per-account id basis and on signup (account creation in Redis and on-chain) an OAuth token is required to help with sybil resistance - [`redis.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/redis.toml) #### FastAuth :::info Closed access beta [FastAuth](fastauth-sdk.md) is currently in a private beta stage. If you want to try it out during this early development stage, please [contact us on Telegram](https://t.me/neardev). ::: This is a configuration for use if you intend to integrate with [FastAuth SDK](fastauth-sdk.md) - It covers gas for user transactions up to a allowance specified in Redis to interact with a whitelisted set of contracts. - Allowances are on a per-account id basis and on signup (account creation in Redis and on-chain) an OAuth token is required to help with sybil resistance - This also makes use of a shared storage functionality on the Near Social DB contract - and a whitelisted sender (`whitelisted_delegate_action_receiver_ids`) - [`fastauth.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/fastauth.toml) #### Pay with fungible tokens This is a configuration for a relayer that ensures there's FTs sent to a burn address used to cover the equivalent amount of gas for user transactions to interact with a whitelisted set of contracts - [`pay_with_ft.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/pay_with_ft.toml) #### Whitelist senders This is a config for a relayer that covers gas for a whitelisted set of users' transactions to interact with a whitelisted set of contracts - [`whitelist_senders.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/whitelist_senders.toml) (`whitelisted_delegate_action_receiver_ids`) #### Shared storage This is a configuration for a relayer that covers BOTH gas AND storage fees for user transactions to interact with a whitelisted set of contracts - be sure to include shared storage logic based on [`shared_storage.rs`](https://github.com/NearSocial/social-db/blob/master/contract/src/shared_storage.rs) in your contract that is being whitelisted - [`shared_storage.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/shared_storage.toml) #### Exchange withdraw This is a configuration for a relayer where an exchange running the relayer covers user withdraw fees when they are withdrawing stablecoins on NEAR (e.g., `USDT` or `USDC`) - [`exchange_withdraw.toml`](https://github.com/near/pagoda-relayer-rs/blob/main/examples/configs/exchange_withdraw.toml) --- # Source: https://docs.near.org/chain-abstraction/meta-transactions.md --- id: meta-transactions title: Meta Transactions description: "Learn about NEP-366 meta transactions on NEAR, allowing users to execute transactions without owning gas tokens by using relayers to cover transaction fees." --- [NEP-366](https://github.com/near/NEPs/pull/366) introduced the concept of meta transactions to Near Protocol. This feature allows users to execute transactions on NEAR without owning any gas or tokens. In order to enable this, users construct and sign transactions off-chain. A third party (the relayer) is used to cover the fees of submitting and executing the transaction. --- ## Overview ![Flow chart of meta transactions](https://raw.githubusercontent.com/near/NEPs/003e589e6aba24fc70dd91c9cf7ef0007ca50735/neps/assets/nep-0366/NEP-DelegateAction.png) _Credits for the diagram go to the NEP authors Alexander Fadeev and Egor Uleyskiy._ The graphic shows an example use case for meta transactions. Alice owns an amount of the fungible token `$FT`. She wants to transfer some to John. To do that, she needs to call `ft_transfer("john", 10)` on an account named `FT`. The problem is, Alice has no NEAR tokens. She only has a NEAR account that someone else funded for her and she owns the private keys. She could create a signed transaction that would make the `ft_transfer("john", 10)` call. But validator nodes will not accept it, because she does not have the necessary Near token balance to purchase the gas. With meta transactions, Alice can create a `DelegateAction`, which is very similar to a transaction. It also contains a list of actions to execute and a single receiver for those actions. She signs the `DelegateAction` and forwards it (off-chain) to a relayer. The relayer wraps it in a transaction, of which the relayer is the signer and therefore pays the gas costs. If the inner actions have an attached token balance, this is also paid for by the relayer. On chain, the `SignedDelegateAction` inside the transaction is converted to an action receipt with the same `SignedDelegateAction` on the relayer's shard. The receipt is forwarded to the account from `Alice`, which will unpacked the `SignedDelegateAction` and verify that it is signed by Alice with a valid Nonce, etc. If all checks are successful, a new action receipt with the inner actions as body is sent to `FT`. There, the `ft_transfer` call finally executes. --- ## Relayer Meta transactions only work with an off-chain service known as `relayer`. Think of it as a server that accepts a `SignedDelegateAction`, does some checks on them and eventually forwards it inside a transaction to the network. :::tip Want to build a relayer? Check out the [Relayer Guide](meta-transactions.md) ::: A relayer may choose to offer their service for free but that's not going to be financially viable long-term. But they could easily have the user pay using other means, outside of Near blockchain. And with some tricks, it can even be paid using fungible tokens on Near. In the example visualized above, the payment is done using $FT. Together with the transfer to John, Alice also adds an action to pay 0.1 $FT to the relayer. The relayer checks the content of the `SignedDelegateAction` and only processes it if this payment is included as the first action. In this way, the relayer will be paid in the same transaction as John. :::warning Keep in mind The payment to the relayer is still not guaranteed. It could be that Alice does not have sufficient `$FT` and the transfer fails. To mitigate, the relayer should check the `$FT` balance of Alice first. ::: Unfortunately, this still does not guarantee that the balance will be high enough once the meta transaction executes. The relayer could waste NEAR gas without compensation if Alice somehow reduces her $FT balance in just the right moment. Some level of trust between the relayer and its user is therefore required.
Technical Details Technically, the end user (client) creates a `SignedDelegateAction` that contains the data necessary to construct a `Transaction`, signs the `SignedDelegateAction` using their key, and send it to the relayer service. When the request is received, the relayer uses its own key to sign a `Transaction` using the fields in the `SignedDelegateAction` as input to create a `SignedTransaction`. The `SignedTransaction` is then sent to the network via RPC call, and the result is sent back to the client. The `Transaction` is executed in such a way that the relayer pays the GAS fees, but all actions are executed as if the user had sent the transaction.
Why using a relayer? ## Why use a Relayer? There are multiple reasons to use a relayer: 1. Your users are new to NEAR and don't have any gas to cover transactions 2. Your users have an account on NEAR, but only have a Fungible Token Balance. They can now use the FT to pay for gas 3. As an enterprise or a large startup you want to seamlessly onboard your existing users onto NEAR without needing them to worry about gas costs and seed phrases 4. As an enterprise or large startup you have a user base that can generate large spikes of user activity that would congest the network. In this case, the relayer acts as a queue for low urgency transactions 5. In exchange for covering the gas fee costs, relayer operators can limit where users spend their assets while allowing users to have custody and ownership of their assets 6. Capital Efficiency: Without relayer if your business has 1M users they would have to be allocated 0.25 NEAR to cover their gas costs totalling 250k NEAR. However, only ~10% of the users would actually use the full allowance and a large amount of the 250k NEAR is just sitting there unused. So using the relayer, you can allocate 50k NEAR as a global pool of capital for your users, which can refilled on an as needed basis.
--- ## Limitations ### Single receiver A meta transaction, like a normal transaction, can only have one receiver. It's possible to chain additional receipts afterwards. But crucially, there is no atomicity guarantee and no roll-back mechanism.
### Accounts must be initialized Any transaction, including meta transactions, must use NONCEs to avoid replay attacks. The NONCE must be chosen by Alice and compared to a NONCE stored on chain. This NONCE is stored on the access key information that gets initialized when creating an account. --- ## Constraints on the actions inside a meta transaction A transaction is only allowed to contain one single delegate action. Nested delegate actions are disallowed and so are delegate actions next to each other in the same receipt. --- ## Gas costs for meta transactions Meta transactions challenge the traditional ways of charging gas for actions. Let's assume Alice uses a relayer to execute actions with Bob as the receiver. 1. The relayer purchases the gas for all inner actions, plus the gas for the delegate action wrapping them. 2. The cost of sending the inner actions and the delegate action from the relayer to Alice's shard will be burned immediately. The condition `relayer == Alice` determines which action `SEND` cost is taken (`sir` or `not_sir`). Let's call this `SEND(1)`. 3. On Alice's shard, the delegate action is executed, thus the `EXEC` gas cost for it is burned. Alice sends the inner actions to Bob's shard. Therefore, we burn the `SEND` fee again. This time based on `Alice == Bob` to figure out `sir` or `not_sir`. Let's call this `SEND(2)`. 4. On Bob's shard, we execute all inner actions and burn their `EXEC` cost. Each of these steps should make sense and not be too surprising. But the consequence is that the implicit costs paid at the relayer's shard are `SEND(1)` + `SEND(2)` + `EXEC` for all inner actions plus `SEND(1)` + `EXEC` for the delegate action. This might be surprising but hopefully with this explanation it makes sense now! --- ## Gas refunds in meta transactions Gas refund receipts work exactly like for normal transaction. At every step, the difference between the pessimistic gas price and the actual gas price at that height is computed and refunded. At the end of the last step, additionally all remaining gas is also refunded at the original purchasing price. The gas refunds go to the signer of the original transaction, in this case the relayer. This is only fair, since the relayer also paid for it. --- ## Balance refunds in meta transactions Unlike gas refunds, the protocol sends balance refunds to the predecessor (a.k.a. sender) of the receipt. This makes sense, as we deposit the attached balance to the receiver, who has to explicitly reattach a new balance to new receipts they might spawn. In the world of meta transactions, this assumption is also challenged. If an inner action requires an attached balance (for example a transfer action) then this balance is taken from the relayer. The relayer can see what the cost will be before submitting the meta transaction and agrees to pay for it, so nothing wrong so far. But what if the transaction fails execution on Bob's shard? At this point, the predecessor is `Alice` and therefore she receives the token balance refunded, not the relayer. This is something relayer implementations must be aware of since there is a financial incentive for Alice to submit meta transactions that have high balances attached but will fail on Bob's shard. --- ## Function Call Access Keys in Meta Transactions [Function Call Access Keys](../protocol/access-keys.md#function-call-keys) are limited to signing transactions for specific methods on a specific contract. Additionally, they can have an allowance, which limits the amount of tokens that can be spent on GAS fees. This limit however can be circumvented by using meta transactions. When a `DelegateAction` is processed in the network, the access key of the sender is checked against the receiver and the methods called. If the access key is allowed to make the call, the action is executed. The allowance however is not checked, as all costs have been covered by the relayer. Hence, the action will be executed even if the allowance is insufficient to cover the costs. --- # Source: https://docs.near.org/tools/near-api.md --- id: near-api title: NEAR API sidebar_label: NEAR API description: "Learn to use APIs in JavaScript, Rust, and Python to interact with the blockchain." --- We offer a collection of language-specific libraries that allow developers to interact with the NEAR blockchain from both frontend and backend applications. The different APIs allow you to perform a variety of actions on the NEAR blockchain, including but not limited to: 1. Create and manage NEAR accounts 2. Call functions on smart contracts 3. Transfer tokens, including native NEAR, Fungible Tokens, Non-Fungible Tokens 4. Sign transactions/meta-transactions/messages and broadcasting them to the network 5. Deploy smart contracts --- ## Available APIs We have APIs available for Javascript, Rust, and Python. Add them to your project using the following commands: ```bash npm i near-api-js ``` ```bash npm i near-kit ``` ```bash cargo add near-api ``` ```shell pip install py-near ``` :::tip Wallet Integration If you are building a web app and need to add Wallet Login on it you will instead need a [`Wallet Connector`](../web3-apps/tutorials/wallet-login) ::: --- ## Account ### Get Balance {#get-balance} Gets the available and staked balance of an account in yoctoNEAR. ``` import { Account, JsonRpcProvider } from "near-api-js"; import { NEAR, FungibleToken } from "near-api-js/tokens"; import { USDT } from "near-api-js/tokens/testnet"; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const accountId: string = 'influencer.testnet'; const account = new Account(accountId, provider); // ------- Fetch NEAR tokens balance ------- const nearTokensBalanceInt: bigint = await account.getBalance(NEAR); console.log("NEAR: ", NEAR.toDecimal(nearTokensBalanceInt, 2)); // ------- Fetch USDT tokens balance ------- const usdtTokensBalanceInt: bigint = await account.getBalance(USDT); console.log("USDT: ", USDT.toDecimal(usdtTokensBalanceInt, 2)); // ------- Fetch REF tokens balance in the smallest units as BigInt ------- const REF = new FungibleToken("ref.fakes.testnet", { decimals: 18, symbol: "REF", name: "REF Token", }); const refTokensBalanceInt: bigint = await account.getBalance(REF); console.log("REF: ", REF.toDecimal(refTokensBalanceInt, 2)); ``` ``` import { Near } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); // Create a connection to testnet (no signing needed for view operations) const near = new Near({ network: "testnet" }); // Get the account ID from environment or use a default const accountId = process.env.ACCOUNT_ID || "example-account.testnet"; // Get NEAR balance for the account const balance = await near.getBalance(accountId); console.log(`Account: ${accountId}`); console.log(`NEAR Balance: ${balance} NEAR`); ``` ``` use near_api::{Account, AccountId, NetworkConfig, Tokens}; #[tokio::main] async fn main() { let my_account_id: AccountId = "example-account.testnet".parse().unwrap(); // Create an account object let my_account = Account(my_account_id.clone()); // Create a connection to the NEAR testnet let network = NetworkConfig::testnet(); // Gets the available, and staked balance in yoctoNEAR let near_balance = Tokens::account(my_account_id.clone()) .near_balance() .fetch_from(&network) .await .unwrap(); println!("{:?}", near_balance); // Account's state, including its code hash and storage usage let account_info = my_account.view().fetch_from(&network).await.unwrap(); println!("{:?}", account_info); } ``` ```python from py_near.account import Account account = Account(account_id="example-account.testnet", rpc_addr="https://rpc.testnet.pagoda.co") await account.startup() account_balance = account.get_balance() ```
### Get State {#get-state} Get basic account information, such as its code hash and storage usage. ``` import { Account, JsonRpcProvider } from "near-api-js"; // Option 1: Gather details through the RPC Provider const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const rpcState = await provider.viewAccount({accountId: "example-account.testnet"}); console.log(rpcState); // Option 2: Use an Account object const account = new Account("example-account.testnet", provider); const accountState = await account.getState(); console.log(accountState); ``` ``` import { Near } from "near-kit"; // Create a connection to testnet const near = new Near({ network: "testnet" }); // Get account balance using the Near instance const accountId = "example-account.testnet"; const balance = await near.getBalance(accountId); console.log(`Account: ${accountId}`); console.log(`Balance: ${balance} NEAR`); ``` ``` let account_info = my_account.view().fetch_from(&network).await.unwrap(); println!("{:?}", account_info); ``` ```python from py_near.account import Account account = Account(account_id="example-account.testnet", rpc_addr="https://rpc.testnet.pagoda.co") await account.startup() account_state = account.fetch_state() ```
### Create Named Account {#create-named-account} To create a named account like `user.testnet`, you need to call the `create_account` function on `near` (or `testnet`), passing as parameters the new account ID, and a public key to add as [FullAccess key](/protocol/access-keys#full-access-keys) ``` import dotenv from "dotenv"; import { Account, JsonRpcProvider, KeyPair, KeyPairString, nearToYocto } from "near-api-js"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const account = new Account(accountId, provider, privateKey); // example-account.testnet // Generate a new accountId and key const newAccountId: string = Date.now() + ".testnet"; const keyPair = KeyPair.fromRandom("ed25519"); const publicKey: string = keyPair.getPublicKey().toString(); await account.createAccount({ newAccountId, publicKey, nearToTransfer: nearToYocto("0") }); console.log(`Created ${newAccountId} with private key ${keyPair.toString()}`); // Option 2: Call `testnet` directly const newAccountId2: string = Date.now() + ".testnet"; await account.callFunction({ contractId: "testnet", methodName: "create_account", args: { "new_account_id": newAccountId2, "new_public_key": publicKey } }); console.log(`Created account ${newAccountId2} with privateKey: ${keyPair.toString()}`); ``` ``` import { Near, generateKey } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Generate a new key pair for the top-level account const keyPair = generateKey(); const publicKey = keyPair.publicKey.toString(); // Create a unique top-level account name using timestamp const newAccountId = `${Date.now()}.testnet`; // Create the top-level account by calling the testnet contract await near.call("testnet", "create_account", { new_account_id: newAccountId, new_public_key: publicKey, }); console.log(`Created top-level account: ${newAccountId}`); console.log(`Private key: ${keyPair.secretKey}`); ``` ``` let private_key = signer::generate_secret_key().unwrap(); let create_account_result = Account::create_account(new_account_id.clone()) // example-account.testnet .fund_myself( account_id.clone(), NearToken::from_millinear(100), // Initial balance for new account in yoctoNEAR ) .public_key(private_key.public_key()).unwrap() .with_signer(signer.clone()) // Signer is the account that is creating the new account .send_to(&network) .await .unwrap(); println!("Private key: {:?}", private_key.to_string()); println!("Public key: {:?}", private_key.public_key().to_string()); println!("{:?}", create_account_result); // Create a sub account ```
Creating an account from a seed phrase You can also create an account via a randomly generated seed phrase. ``` let (seed_phrase, seed_pk) = signer::generate_seed_phrase().unwrap(); let create_account_result = Account::create_account(new_account_id.clone()) // example-account.testnet .fund_myself( account_id.clone(), NearToken::from_millinear(100), // Initial balance for new account in yoctoNEAR ) .public_key(seed_pk).unwrap() .with_signer(signer.clone()) // Signer is the account that is creating the new account .send_to(&network) .await .unwrap(); println!("Seed phrase: {:?}", seed_phrase); println!("{:?}", create_account_result); } ```
```python await account.function_call("testnet", "create_account", {"new_account_id": "example-account.testnet", "new_public_key": "ed25519:..."}, "30000000000000", 1 * NEAR) ```

### Create Sub-Account {#create-sub-account} Accounts on NEAR can create sub-accounts under their own namespace, which is useful for organizing accounts by purpose — for example, `project.user.testnet`. ``` import dotenv from "dotenv"; import { Account, JsonRpcProvider, KeyPair, KeyPairString, nearToYocto } from "near-api-js"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const account = new Account(accountId, provider, privateKey); // example-account.testnet // Generate a new account prefix and key pair const prefix: string = Date.now().toString(); const keyPair = KeyPair.fromRandom("ed25519"); const publicKey: string = keyPair.getPublicKey().toString(); await account.createSubAccount({ accountOrPrefix: prefix, // prefix for the sub account (e.g. sub.near.testnet) publicKey, // ed25519:2ASWc... nearToTransfer: nearToYocto("0") // Initial balance for new account in yoctoNEAR }); console.log(`Created ${prefix}.${accountId} with private key ${keyPair.toString()}`); ``` ``` import { Near, generateKey } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Generate a new key pair for the subaccount const keyPair = generateKey(); const publicKey = keyPair.publicKey.toString(); // Create a unique subaccount name using timestamp const prefix = Date.now().toString(); const newAccountId = `${prefix}.${accountId}`; // Create the subaccount using transaction builder with chained actions await near .transaction(accountId) .createAccount(newAccountId) .transfer(newAccountId, "0.1 NEAR") .addKey(publicKey, { type: "fullAccess" }) .send(); console.log(`Created subaccount: ${newAccountId}`); console.log(`Private key: ${keyPair.secretKey}`); ``` ``` let private_key = signer::generate_secret_key().unwrap(); let create_sub_account_result = Account::create_account(sub_account_id.clone()) // sub.example-account.testnet .fund_myself( account_id.clone(), NearToken::from_millinear(100), // Initial balance for sub account in yoctoNEAR ) .public_key(private_key.public_key()).unwrap() .with_signer(signer.clone()) // Signer is the account that is creating the sub account .send_to(&network) .await .unwrap(); println!("Private key: {:?}", private_key.to_string()); println!("Public key: {:?}", private_key.public_key().to_string()); println!("{:?}", create_sub_account_result); } ``` Create a sub-account and fund it with your main account: ```python from py_near.account import Account from py_near.dapps.core import NEAR account = Account(account_id="example-account.testnet", private_key="ed25519:...", rpc_addr="https://rpc.testnet.pagoda.co") await account.startup() res = account.create_account(account_id="sub.example-account.testnet", public_key="...", initial_balance=1 * NEAR)) ``` :::info Parent accounts have **no control** over their sub-accounts, they are completely independent. :::
### Delete Account {#delete-account} Accounts on NEAR can delete themselves, transferring any remaining balance to a specified beneficiary account. ``` const accountToDelete = new Account(deleteMe, provider, privateKey); // Delete the account const beneficiary: string = process.env.ACCOUNT_ID!; await accountToDelete.deleteAccount(beneficiary); console.log(`Account ${deleteMe} was deleted`); ``` ``` import { Near, generateKey } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Generate a new key pair for the account to be deleted const keyPair = generateKey(); const publicKey = keyPair.publicKey.toString(); // Create a unique top-level account name using timestamp const accountToDelete = `${Date.now()}.testnet`; // First, create the account by calling the testnet contract await near.call("testnet", "create_account", { new_account_id: accountToDelete, new_public_key: publicKey, }); console.log(`Created account: ${accountToDelete}`); // Fund the new account so it can pay for the delete transaction await near.send(accountToDelete, "0.01 NEAR"); console.log(`Funded account with 0.01 NEAR`); // Create a new Near instance with the new account's key to delete it const nearForNewAccount = new Near({ network: "testnet", privateKey: keyPair.secretKey, defaultSignerId: accountToDelete, }); // Delete the account, sending remaining balance to beneficiary await nearForNewAccount .transaction(accountToDelete) .deleteAccount({ beneficiary: accountId }) .send(); console.log(`Deleted account: ${accountToDelete}`); console.log(`Beneficiary: ${accountId}`); ``` ``` // Delete the account with account Id of the account object let delete_account_result = account_to_delete .delete_account_with_beneficiary(beneficiary_account_id.clone()) // example-beneficiary.testnet .with_signer(signer.clone()) // Signer is the account that is being deleted .send_to(&network) .await .unwrap(); println!("{:?}", delete_account_result); } ``` :::info Deleting an account **DOES NOT** affect its sub-accounts - they will remain active. ::: :::danger The Beneficiary Only Receives NEAR Tokens Fungible (FTs) or Non-Fungible tokens (NFTs) held by the account **ARE NOT** automatically transferred. These tokens are still associated with the account, even after the account is deleted. Make sure to transfer those assets manually before deletion, or you're risking losing them permanently! Once the account is gone, those assets are effectively stuck unless the same account is recreated by anyone (not necessarily you). ::: :::danger Make Sure the Beneficiary Account Exists If the beneficiary account doesn't exist, all NEAR tokens sent to it will be burned. Double-check the account ID before proceeding. ::: --- ## Transactions ### Send Tokens {#send-tokens} Accounts can transfer different types of tokens to other accounts, including the native NEAR token and [NEP-141](https://github.com/near/NEPs/tree/master/neps/nep-0141.md) fungible tokens. ``` import dotenv from "dotenv"; import { Account, JsonRpcProvider, KeyPairString } from "near-api-js"; import { NEAR, FungibleToken } from "near-api-js/tokens"; import { USDT } from "near-api-js/tokens/testnet"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const account = new Account(accountId, provider, privateKey); // example-account.testnet // ------- Send NEAR tokens to another account ------- const sendNearTokensResult = await account.transfer({ token: NEAR, amount: NEAR.toUnits("0.1"), receiverId: "influencer.testnet" }); console.log(sendNearTokensResult); // ------- Send USDT tokens to another account ------- // if a user isn't registered, the transfer will fail // it a user is already registered, we'll just get funds back await USDT.registerAccount({ accountIdToRegister: "influencer.testnet" as unknown as Account, fundingAccount: account, }).catch(() => {}); // ignore errors if already registered // Use https://testnet.rhea.finance/#near|usdtt.fakes.testnet to get USDT token const sendUsdtTokensResult = await account.transfer({ token: USDT, amount: USDT.toUnits("1"), // Amount of USDT being sent receiverId: "influencer.testnet" }); console.log(sendUsdtTokensResult); // ------- Send REF tokens to another account ------- // Use https://testnet.rhea.finance/#near|ref.fakes.testnet to get REF tokens const REF = new FungibleToken("ref.fakes.testnet", { decimals: 18, symbol: "REF", name: "REF Token", }); await REF.registerAccount({ accountIdToRegister: "influencer.testnet" as unknown as Account, fundingAccount: account, }); const sendREFsResult = await account.transfer({ token: REF, amount: REF.toUnits("1"), // Amount of REF tokens being sent receiverId: "influencer.testnet" }).catch(() => {}); // ignore errors if already registered console.log(sendREFsResult); ``` ``` import { Near } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Send NEAR tokens to another account const result = await near.send("receiver-account.testnet", "0.1 NEAR"); console.log("Transfer result:", result); ``` ``` let send_tokens_result = Tokens::account(account_id.clone()) // example-account.testnet .send_to("receiver-account.testnet".parse().unwrap()) .near(NearToken::from_near(1)) .with_signer(signer.clone()) .send_to(&network) .await .unwrap(); println!("{:?}", send_tokens_result); ``` ```python from py_near.account import Account from py_near.dapps.core import NEAR account = Account(account_id="example-account.testnet", private_key="ed25519:...", rpc_addr="https://rpc.testnet.pagoda.co") await account.startup() await account.send_money("receiver-account.testnet", 1 * NEAR)) ```
### Call Function A smart contract exposes its methods, and making a function call that modifies state requires a `Signer`/`KeyPair`. You can optionally attach a `NEAR` deposit to the call. ``` import dotenv from "dotenv"; import { Account, JsonRpcProvider, teraToGas, KeyPairString, nearToYocto } from "near-api-js"; // Create an account object dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a testnet provider const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // For read only calls, you can use the provider directly const messages = await provider.callFunction({ contractId: 'guestbook.near-examples.testnet', method: "get_messages", args: {}, }); console.log(messages); // To modify state, you need an account to sign the transaction const account = new Account(accountId, provider, privateKey); // Call the contract await account.callFunction({ contractId: 'guestbook.near-examples.testnet', methodName: "add_message", args: { text: "Hello!" }, gas: teraToGas('30'), deposit: nearToYocto('0.1'), }); ``` :::tip Typed Result When using Typescript, you can type the return of `callFunction` ::: ``` import { nearToYocto, Account, Contract, JsonRpcProvider, KeyPairString } from "near-api-js"; import abi from "./guestbook.abi.js"; import dotenv from "dotenv"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const account = new Account(accountId, provider, privateKey); // example-account.testnet // Make a function call that modifies state const contract = new Contract({ contractId: "guestbook.near-examples.testnet", provider: provider, abi: abi, }); contract.call.add_message({ deposit: nearToYocto("0.1"), args: { text: "Hello, NEAR!" }, account: account, }); // TypeScript infers the type of this method // Make a read-only function call const totalMessages = await contract.view.total_messages(); console.log({ totalMessages }); // Send a tx to add message const result = await contract.call.add_message({ account, args: { text: "Hello, world!" }, }); console.log({ result }); ``` ``` import dotenv from "dotenv"; import { Near } from "near-kit"; // Create a connection to testnet with credentials dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; const near = new Near({ network: "testnet", privateKey, defaultSignerId: accountId }); // For read only calls, you can use the provider directly const messages = await near.view( 'guestbook.near-examples.testnet', 'get_messages', {} ); console.log(messages); // To modify state, call the contract method await near.call( 'guestbook.near-examples.testnet', 'add_message', { text: "Hello!" }, { gas: "30 Tgas" } ); ``` :::tip Typed Result When using Typescript, you can type the return of `Near.view` and `Near.call` ::: ``` import { Near } from "near-kit"; import dotenv from "dotenv"; dotenv.config(); // Create a connection to testnet with credentials from environment const near = new Near({ network: "testnet", privateKey: process.env.PRIVATE_KEY, defaultSignerId: process.env.ACCOUNT_ID }); // Define the contract interface const guestbook = near.contract("guestbook.near-examples.testnet"); // View: get total messages const totalMessages = await guestbook.view.total_messages(); console.log({ totalMessages }); // Call: add a message const result = await guestbook.call.add_message({ text: "Hello from near-kit!" }, { gas: "30 Tgas" }); console.log({ result }); ``` ``` let contract_id: AccountId = "guestbook.near-examples.testnet".parse().unwrap(); let contract = Contract(contract_id.clone()); ``` ``` let args = json!({ "text": "Hello, world!" }); let function_call_result = contract .call_function("add_message", args) .unwrap() .transaction() .deposit(NearToken::from_near(1)) .with_signer(account_id.clone(), signer.clone()) // Signer is the account that is calling the function .send_to(&network) .await .unwrap(); println!("{:?}", function_call_result); ``` ```python await account.function_call("usn.near", "ft_transfer", {"receiver_id": "bob.near", "amount": "1000000000000000000000000"}) ```
### Batch Actions You can send multiple [actions](../protocol/transaction-anatomy.md#actions) in a batch to a **single** receiver. If one action fails then the entire batch of actions will be reverted. ``` import dotenv from "dotenv"; import { Account, JsonRpcProvider, actions, teraToGas, nearToYocto, KeyPairString } from "near-api-js"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const account = new Account(accountId, provider, privateKey); // example-account.testnet // Send the batch of actions const batchActionsResult = await account.signAndSendTransaction({ receiverId: "counter.near-examples.testnet", actions: [ actions.functionCall("increment", {}, teraToGas("30"), 0n), actions.transfer(nearToYocto("0.001")) ], }); console.log(batchActionsResult); ``` ``` import { Near } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Batch multiple actions in one transaction const result = await near.transaction(accountId) .functionCall("counter.near-examples.testnet", "increment", {}, { gas: "30 Tgas" }) .transfer("counter.near-examples.testnet", "0.001 NEAR") .send(); console.log("Batch actions result:", result); ``` ``` // Prepare the actions let call_action = Action::FunctionCall(Box::new(FunctionCallAction { method_name: "increment".to_string(), args: serde_json::json!({}).to_string().into_bytes(), gas: 30_000_000_000_000, deposit: 0, })); let transfer_action = Action::Transfer(TransferAction { deposit: 1_000_000_000_000_000_000_000_000, }); // Transfer 1 NEAR // Send the batch of actions let batch_actions_result = Transaction::construct( account_id.clone(), "counter.near-examples.testnet".parse().unwrap(), ) .add_actions(vec![call_action, transfer_action]) .with_signer(signer) .send_to(&network) .await .unwrap(); println!("{:?}", batch_actions_result); ```
### Simultaneous Transactions The only way to have true simultaneous transactions is to use multiple access keys on a same account. Each access key maintains its own nonce, allowing transactions signed with different keys to be processed in parallel: ``` import dotenv from "dotenv"; import { NEAR } from "near-api-js/tokens"; import { Account, actions, JsonRpcProvider, KeyPair, MultiKeySigner, KeyPairString } from "near-api-js"; dotenv.config(); // Loads .env const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create 10 keys and add them to the account const account = new Account(accountId, provider, privateKey); const keys: KeyPair[] = []; const txActions: ReturnType[] = []; for (let j = 0; j < 10; j++) { const newKeyPair = KeyPair.fromRandom('ed25519'); keys.push(newKeyPair); txActions.push(actions.addFullAccessKey(newKeyPair.getPublicKey())); } await account.signAndSendTransaction({ receiverId: accountId, actions: txActions }); // Send NEAR tokens using multiple keys const multiKeySigner = new MultiKeySigner(keys); const multiAccount = new Account(accountId, provider, multiKeySigner); const transfers = [...Array(100)].map(() => multiAccount.transfer({ token: NEAR, amount: NEAR.toUnits("0.001"), receiverId: "influencer.testnet" }) ); const sendNearTokensResults = await Promise.all(transfers); sendNearTokensResults.forEach(result => console.log(result)); ``` ``` import { Near, generateKey, RotatingKeyStore } from "near-kit"; import dotenv from "dotenv"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a RotatingKeyStore for concurrent transactions const keyStore = new RotatingKeyStore(); // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Add multiple keys to the account for concurrent transactions const keys = []; for (let i = 0; i < 5; i++) { const newKey = generateKey(); keys.push(newKey); // Add key to the account on-chain await near .transaction(accountId) .addKey(newKey.publicKey.toString(), { type: "fullAccess" }) .send(); // Add key to the rotating store await keyStore.add(accountId, newKey); } console.log(`Added ${keys.length} keys to account`); // Create a new Near client with the rotating keystore const nearWithRotating = new Near({ network: "testnet", keyStore, defaultSignerId: accountId, }); // Send multiple concurrent transactions // The RotatingKeyStore will distribute keys to avoid nonce conflicts const transfers = Array.from({ length: 10 }, (_, i) => nearWithRotating.send("influencer.testnet", "0.001 NEAR") ); const results = await Promise.all(transfers); console.log(`Completed ${results.length} concurrent transfers`); // Clean up: delete the keys we added for (const key of keys) { await near .transaction(accountId) .deleteKey(accountId, key.publicKey.toString()) .send(); } console.log("Cleaned up keys"); ``` ``` // Prepare the transactions let args = serde_json::to_vec(&json!({ "text": "Hello, world!" })) .unwrap(); let action1 = Action::FunctionCall(Box::new(FunctionCallAction { method_name: "add_message".to_string(), args, gas: 100_000_000_000_000, deposit: 0, })); let tx1 = Transaction::construct( account_id.clone(), "guestbook.near-examples.testnet".parse().unwrap(), ) .add_action(action1) .with_signer(signer.clone()); let action2 = Action::FunctionCall(Box::new(FunctionCallAction { method_name: "increment".to_string(), args: vec![], gas: 100_000_000_000_000, deposit: 0, })); let tx2 = Transaction::construct( account_id.clone(), "counter.near-examples.testnet".parse().unwrap(), ) .add_action(action2) .with_signer(signer.clone()); // Send the transactions simultaneously let (tx1_result, tx2_result) = tokio::join!(tx1.send_to(&network), tx2.send_to(&network)); println!("{:?}", tx1_result); ``` ```python import asyncio from py_near.account import Account account = Account(account_id="example-account.testnet", private_key="ed25519:...", rpc_addr="https://rpc.testnet.pagoda.co") await account.startup() # Prepare the transactions tx1 = account.function_call("guestbook.near-examples.testnet", "add_message", { "text": "Hello, world!" }) tx2 = account.function_call("counter.near-examples.testnet", "increment", {}) # Send the transactions simultaneously const transactionsResults = await asyncio.gather(tx1, tx2) ``` :::warning Keep in mind Simultaneous execution means there’s no guarantee of order or success. Any transaction may fail independently. If your use case requires strict ordering, then you should stick to sending transactions sequentially from a single key. :::
### Deploy a Contract {#deploy-a-contract} On NEAR, a smart contract is deployed as a WASM file. Every account has the potential to become a contract — you simply need to deploy code to it. ``` import { Account, JsonRpcProvider, KeyPairString } from "near-api-js"; import fs from "fs"; import dotenv from "dotenv"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const account = new Account(accountId, provider, privateKey); // example-account.testnet // Deploy a contract to the account const result = await account.deployContract( fs.readFileSync("../contracts/contract.wasm") // Path of contract WASM relative to the working directory ); console.log(result); ``` ``` import { Near } from "near-kit"; import dotenv from "dotenv"; import fs from "fs"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Deploy a contract to the account const wasmCode = fs.readFileSync("../contracts/contract.wasm"); const result = await near.transaction(accountId) .deployContract(accountId, wasmCode) .send(); console.log("Deploy contract result:", result); ``` Note that the `signer` here needs to be a signer for the same `account_id` as the one used to construct the `Contract` object. ``` let deploy_result = Contract::deploy(account_id.clone()) .use_code(include_bytes!("../../contracts/contract.wasm").to_vec()) // Path of contract WASM relative to this file .without_init_call() .with_signer(signer) // Signer is the account that is deploying the contract .send_to(&network) .await .unwrap(); println!("{:?}", deploy_result); } ``` ```python import asyncio from py_near.account import Account account = Account(account_id="example-account.testnet", private_key="ed25519:...", rpc_addr="https://rpc.testnet.pagoda.co") await account.startup() with open("contract.wasm", "rb") as f: contract_code = f.read() await account.deploy_contract(contract_code) ```
### Deploy a Global Contract {#deploy-a-global-contract} [Global contracts](../smart-contracts/global-contracts.md) allow smart contracts to be deployed once and reused by any account without incurring high storage costs. There are two ways to reference a global contract: - **[By account](../smart-contracts/global-contracts.md#reference-by-account):** The contract code is tied to another account. - **[By hash](../smart-contracts/global-contracts.md#reference-by-hash):** You reference the contract by its immutable code hash. ``` import { Account, JsonRpcProvider, KeyPair, KeyPairString } from "near-api-js"; import { NEAR } from "near-api-js/tokens"; import { readFileSync } from "fs"; import dotenv from "dotenv"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const deployer = new Account(accountId, provider, privateKey); // example-account.testnet // Path of contract WASM relative to the working directory const wasm: Uint8Array = new Uint8Array(readFileSync("../contracts/contract.wasm")); const deployResult = await deployer.deployGlobalContract(wasm, "accountId"); console.log(deployResult); ``` ``` import { Account, JsonRpcProvider, KeyPair, KeyPairString, baseEncode } from "near-api-js"; import { NEAR } from "near-api-js/tokens"; import { readFileSync } from "fs"; import { createHash } from "crypto"; import dotenv from "dotenv"; dotenv.config(); const privateKey = process.env.PRIVATE_KEY! as KeyPairString; const accountId: string = process.env.ACCOUNT_ID!; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Create an account object const deployer = new Account(accountId, provider, privateKey); // example-account.testnet // Path of contract WASM relative to the working directory const wasm: Uint8Array = new Uint8Array(readFileSync("../contracts/contract.wasm")); const deployResult = await deployer.deployGlobalContract(wasm, "codeHash"); console.log(deployResult); await provider.viewTransactionStatus({ txHash: deployResult.transaction.hash, accountId: deployer.accountId }); ``` ``` import { Near, generateKey } from "near-kit"; import dotenv from "dotenv"; import fs from "fs"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Read the contract WASM const wasmCode = fs.readFileSync("../contracts/contract.wasm"); // Publish as a global contract (identified by account ID) // Other accounts can use this contract without deploying their own copy const publishResult = await near .transaction(accountId) .publishContract(wasmCode, { identifiedBy: "account" }) .send(); console.log("Published global contract:", publishResult.transaction.hash); ``` ``` import { Near, generateKey } from "near-kit"; import dotenv from "dotenv"; import fs from "fs"; import { createHash } from "crypto"; // Load environment variables dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; // Create a connection to testnet with signing capabilities const near = new Near({ network: "testnet", privateKey: privateKey, // ed25519:5Fg2... defaultSignerId: accountId, // example-account.testnet }); // Read the contract WASM const wasmCode = fs.readFileSync("../contracts/contract.wasm"); // Calculate the code hash (SHA-256) const codeHash = createHash("sha256").update(wasmCode).digest(); // Publish as a global contract (identified by code hash) // This creates an immutable contract reference const publishResult = await near .transaction(accountId) .publishContract(wasmCode, { identifiedBy: "hash" }) .send(); console.log("Published global contract by hash:", publishResult.transaction.hash); ``` Once you've created an Account instance, you can deploy your regular contract as a global contract. Let’s look at an example of deploying a global contract by account. To do this, use the `deploy_global_contract_code` function and use the method `as_account_id`, along with the contract’s code bytes. ```rust let global_account_id: AccountId = "nft-contract.testnet".parse().unwrap(); let code = std::fs::read("path/to/your/contract.wasm").unwrap(); let signer = Signer::new(Signer::from_secret_key(private_key)).unwrap(); let result: FinalExecutionOutcomeView = Contract::deploy_global_contract_code(code) .as_account_id(global_account_id) .with_signer(signer) .send_to_testnet() .await.unwrap(); ``` See full example on GitHub Let’s look at an example of deploying a global contract by hash. To do this, use the `deploy_global_contract_code` function and use the method `as_hash`, along with the contract’s code bytes. ```rust let account_id: AccountId = "my-account.testnet".parse().unwrap(); let code = std::fs::read("path/to/your/contract.wasm").unwrap(); let signer = Signer::new(Signer::from_secret_key(private_key)).unwrap(); let result: FinalExecutionOutcomeView = Contract::deploy_global_contract_code(code) .as_hash() .with_signer(account_id, signer) .send_to_testnet() .await.unwrap(); ``` See full example on GitHub ### Use a Global Contract Once a [global contract](../smart-contracts/global-contracts.md) has been [deployed](#deploy-a-global-contract), let’s see how you can reference and use it from another account. ``` const consumer = new Account( consumerAccountId, provider, key.toString() as KeyPairString ); await consumer.useGlobalContract({ accountId: deployer.accountId }); const contract = await consumer.getContractCode(); console.log("Size", contract.code.length, "Hash", contract.hash); ``` ``` const consumer = new Account( consumerAccountId, provider, key.toString() as KeyPairString ); const useResult = await consumer.useGlobalContract({ codeHash: Uint8Array.from(Buffer.from(hash, 'base64')), }); console.log(useResult); await provider.viewTransactionStatus({ txHash: useResult.transaction.hash, accountId: consumer.accountId }); const contract = await consumer.getContractCode(); console.log("Size", contract.code.length, "Hash", contract.hash); // delete consumer account and refund deployer ``` ``` // Create a consumer account that will use the global contract const consumerKey = generateKey(); const consumerAccountId = `${Date.now()}.${accountId}`; await near .transaction(accountId) .createAccount(consumerAccountId) .transfer(consumerAccountId, "0.1 NEAR") .addKey(consumerKey.publicKey.toString(), { type: "fullAccess" }) .send(); console.log("Created consumer account:", consumerAccountId); // Consumer uses the global contract by referencing the publisher's account ID const consumerNear = new Near({ network: "testnet", privateKey: consumerKey.secretKey, defaultSignerId: consumerAccountId, }); await consumerNear .transaction(consumerAccountId) .deployFromPublished({ accountId: accountId }) .send(); console.log("Consumer is now using global contract from:", accountId); // Clean up: delete consumer account await consumerNear .transaction(consumerAccountId) .deleteAccount({ beneficiary: accountId }) .send(); console.log("Cleaned up consumer account"); ``` ``` // Create a consumer account const consumerKey = generateKey(); const consumerAccountId = `${Date.now()}.${accountId}`; const createResult = await near .transaction(accountId) .createAccount(consumerAccountId) .transfer(consumerAccountId, "0.1 NEAR") .addKey(consumerKey.publicKey.toString(), { type: "fullAccess" }) .send(); await near.getTransactionStatus(createResult.transaction.hash, accountId, "FINAL"); console.log("Created consumer account:", consumerAccountId); // Consumer uses the global contract by referencing the code hash const consumerNear = new Near({ network: "testnet", privateKey: consumerKey.secretKey, defaultSignerId: consumerAccountId, }); await consumerNear .transaction(consumerAccountId) .deployFromPublished({ codeHash: codeHash }) .send(); console.log("Consumer is now using global contract by hash"); // Clean up: delete consumer account await consumerNear .transaction(consumerAccountId) .deleteAccount({ beneficiary: accountId }) .send(); console.log("Cleaned up consumer account"); ``` To reference a global contract by account, you need to call the `use_global_account_id` function and pass the source `accountId` where the contract was originally deployed. ```rust let global_account_id: AccountId = "nft-contract.testnet".parse().unwrap(); let my_account_id: AccountId = "my-contract.testnet".parse().unwrap(); let my_signer = Signer::new(Signer::from_secret_key(private_key)).unwrap(); let result: FinalExecutionOutcomeView = Contract::deploy(my_account_id) .use_global_account_id(global_account_id) .without_init_call() .with_signer(my_signer) .send_to_testnet() .await.unwrap(); ``` See full example on GitHub To reference a global contract by hash, you need to call the `use_global_hash` function and pass the source `hash` of the original contract. ```rust let global_hash: types::CryptoHash = "DxfRbrjT3QPmoANMDYTR6iXPGJr7xRUyDnQhcAWjcoFF".parse().unwrap(); let account_id: AccountId = "my-contract.testnet".parse().unwrap(); let signer = Signer::new(Signer::from_secret_key(private_key)).unwrap(); let result: FinalExecutionOutcomeView = Contract::deploy(account_id) .use_global_hash(global_hash) .without_init_call() .with_signer(signer) .send_to_testnet() .await.unwrap(); ``` See full example on GitHub --- ## View Function View functions are read-only methods on a smart contract that do not modify state. You can call them without using an account or signing a transaction. ``` // Create a testnet provider const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // For read only calls, you can use the provider directly const messages = await provider.callFunction({ contractId: 'guestbook.near-examples.testnet', method: "get_messages", args: {}, }); console.log(messages); ``` :::tip Typed Result When using Typescript, you can type the return of `callFunction` ::: ``` import dotenv from "dotenv"; import { Near } from "near-kit"; // Create a connection to testnet with credentials dotenv.config(); const privateKey = process.env.PRIVATE_KEY; const accountId = process.env.ACCOUNT_ID; const near = new Near({ network: "testnet", privateKey, defaultSignerId: accountId }); // For read only calls, you can use the provider directly const messages = await near.view( 'guestbook.near-examples.testnet', 'get_messages', {} ); console.log(messages); ``` :::tip Typed Result When using Typescript, you can type the return of `Near.view` ::: ``` // Create contract object let contract_id: AccountId = "guestbook.near-examples.testnet".parse().unwrap(); let contract = Contract(contract_id.clone()); // Make a view call to a contract let view_call_result: Data = contract .call_function("total_messages", ()) .unwrap() .read_only() .fetch_from(&network) .await .unwrap(); println!("{:?}", view_call_result.data); ``` ```python view_call_result = await account.view_function("guestbook.near-examples.testnet", "total_messages", {}) # If args are required, they can be passed in like this in the 3rd argument: # { # "from_index": "0", # "limit": "10" # } print(view_call_result) ``` --- ## Keys ### Get All Access Keys {#get-all-access-keys} ``` // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // Query keys with the provider const keysProvider = await provider.viewAccessKeyList({ accountId }); console.log(keysProvider); ``` ``` let keys = account.list_keys().fetch_from(&network).await.unwrap(); println!("{:?}", keys); ``` ```python keys = await account.get_access_key_list() ```
### Add Full Access Key {#add-full-access-key} A [Full Access key](/protocol/access-keys.md#full-access-keys) grants complete control over the account. Anyone with this key can transfer funds, sign transactions, interact with contracts, or even delete the account entirely. ``` // New keys const fullKeyPair = KeyPair.fromRandom("ed25519"); const fnKeyPair = KeyPair.fromRandom("ed25519"); // Add a Full Access Key await account.addFullAccessKey( fullKeyPair.getPublicKey().toString() // ed25519:2ASWc... ); ``` ``` // Generate new key pairs const fullAccessKey = generateKey(); const functionCallKey = generateKey(); console.log("Generated Full Access Key:", fullAccessKey.publicKey.toString()); console.log("Generated Function Call Key:", functionCallKey.publicKey.toString()); // Add a Full Access Key using transaction builder await near .transaction(accountId) .addKey(fullAccessKey.publicKey.toString(), { type: "fullAccess" }) .send(); ``` ``` // Add full access key let new_full_private_key = signer::generate_secret_key().unwrap(); // Get the private key from the keypair let add_full_key_result = account .add_key(AccessKeyPermission::FullAccess, new_full_private_key.public_key()) .with_signer(signer.clone()) .send_to(&network) .await .unwrap(); let new_full_public_key = new_full_private_key.public_key().to_string(); println!("{:?}", new_full_public_key); println!("{:?}", add_full_key_result); // Add function call access key ``` ```python keys = await account.add_full_access_public_key("5X9WvUbRV3aSd9Py1LK7HAndqoktZtcgYdRjMt86SxMj") ```
### Add Function Call Key {#add-function-call-key} A [Function Call access key](/protocol/access-keys.md#function-call-keys) is designed specifically to sign transactions that include only [`functionCall` actions](/protocol/transaction-anatomy#actions) to a specific contract. You can further restrict this key by: - Limiting which method names can be called - Capping the amount of `NEAR` the key can spend on transaction fees ``` // Add Function Call Access Key await account.addFunctionCallAccessKey({ publicKey: fnKeyPair.getPublicKey(), // The new public key ed25519:2ASWc... contractId: "example-contract.testnet", // Contract this key is allowed to call (optional) methodNames: ["example_method"], // Methods this key is allowed to call (optional) allowance: NEAR.toUnits("0.25") // Gas allowance key can use to call methods (optional) }); ``` ``` // Add a Function Call Access Key using transaction builder await near .transaction(accountId) .addKey(functionCallKey.publicKey.toString(), { type: "functionCall", receiverId: "example-contract.testnet", methodNames: ["example_method"], allowance: "0.25 NEAR", }) .send(); console.log(`Added Function Call Key: ${functionCallKey.publicKey.toString()}`); ``` ``` // Add function call access key let new_function_call_key = AccessKeyPermission::FunctionCall(FunctionCallPermission { allowance: Some(250_000_000_000_000_000_000_000), // Gas allowance key can use to call methods (optional) receiver_id: "example-contract.testnet".to_string(), // Contract this key is allowed to call method_names: vec!["example_method".to_string()], // Methods this key is allowed to call }); let new_function_private_key = signer::generate_secret_key().unwrap(); let add_function_key_result = account .add_key(new_function_call_key, new_function_private_key.public_key()) .with_signer(signer.clone()) .send_to(&network) .await .unwrap(); let new_function_public_key = new_function_private_key.public_key().to_string(); println!("{:?}", new_function_public_key); println!("{:?}", add_function_key_result); // Delete full access key ``` ```python await account.add_public_key( "5X9WvUbRV3aSd9Py1LK7HAndqoktZtcgYdRjMt86SxMj", "example-contract.testnet", # Contract this key is allowed to call ["example_method"], # Methods this key is allowed to call (optional) 0.25 * NEAR # Gas allowance key can use to call methods (optional) ) ``` :::tip For security reasons, Function Call access keys **can only be used with function calls that attach zero `NEAR` tokens. Any attempt to include a deposit will result in a failed transaction. :::
### Delete Access Key {#delete-access-key} Accounts on NEAR can delete their own keys. ``` await account.deleteKey(fnKeyPair.getPublicKey().toString()); await account.deleteKey(fullKeyPair.getPublicKey().toString()); ``` ``` const nearWithNewKey = new Near({ network: "testnet", privateKey: fullAccessKey.secretKey, defaultSignerId: accountId, }); await nearWithNewKey .transaction(accountId) .deleteKey(accountId, functionCallKey.publicKey.toString()) .send(); ``` ``` // Delete full access key let public_key_to_delete = new_full_public_key; let delete_full_key_result = account .delete_key(public_key_to_delete.parse().unwrap()) .with_signer(signer.clone()) .send_to(&network) .await .unwrap(); println!("{:?}", delete_full_key_result); } ``` ```python await account.delete_public_key("5X9WvUbRV3aSd9Py1LK7HAndqoktZtcgYdRjMt86SxMj") ``` :::danger Be very careful when deleting keys, remove all keys from an account and you will lose access to the account permanently ::: --- ## Validate Message Signatures Users can sign messages using the `wallet-selector` `signMessage` method, which returns a signature. This signature can be verified using the following code: ``` import { verifyMessage } from "near-api-js/nep413"; import { JsonRpcProvider } from "near-api-js"; // Create a connection to testnet RPC const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); // This is the challenge given to the user to sign const MESSAGE: string = "log me in"; const APP: string = "http://localhost:3000"; const CHALLENGE: Buffer = Buffer.from(Array.from(Array(32).keys())); // This is the object returned by `wallet.signMessage` in wallet selector const walletReturn = { "signature": "IfModLa3g3czlyPhkg/LSkTFSy7XCGreStZJTDIO1m3viEnYFLdXfpz1gYUVKYv3W2vwcV77TmGEzc9y0Nz+AA==", "accountId": "maguila.testnet", "publicKey": "ed25519:AtH7GEjv2qmBVoT8qoRhWXizXM5CC12DC6tiqY9iNoRm" }; await verifyMessage({ signerAccountId: walletReturn.accountId, signerPublicKey: walletReturn.publicKey, signature: new Uint8Array(Buffer.from(walletReturn.signature, 'base64')), payload: { message: MESSAGE, recipient: APP, nonce: CHALLENGE }, provider }); ``` ``` import { Near, verifyNep413Signature } from "near-kit"; // Create Near client for testnet (no signing needed for verification) const near = new Near({ network: "testnet" }); // The challenge given to the user to sign const MESSAGE = "log me in"; const APP = "http://localhost:3000"; const CHALLENGE = Buffer.from(Array.from(Array(32).keys())); // The object returned by wallet.signMessage in wallet selector const signedMessage = { signature: "IfModLa3g3czlyPhkg/LSkTFSy7XCGreStZJTDIO1m3viEnYFLdXfpz1gYUVKYv3W2vwcV77TmGEzc9y0Nz+AA==", accountId: "maguila.testnet", publicKey: "ed25519:AtH7GEjv2qmBVoT8qoRhWXizXM5CC12DC6tiqY9iNoRm", }; // Verify the signature const isValid = await verifyNep413Signature( signedMessage, { message: MESSAGE, recipient: APP, nonce: CHALLENGE, }, { near, maxAge: Infinity }, // Disable timestamp check for this demo ); console.log("Signature valid:", isValid); ``` --- ## Additional resources - [Documentation](https://near.github.io/near-api-js) - [Github](https://github.com/near/near-api-js) - [Full Examples](https://github.com/near-examples/near-api-examples/tree/main) - [Github](https://github.com/r-near/near-kit/tree/main) - [Full Examples](https://github.com/near-examples/near-api-examples/tree/main/near-kit) - [Documentation](https://docs.rs/near-api/latest/near_api/) - [Github](https://github.com/near/near-api-rs) - [Full Examples](https://github.com/near-examples/near-api-examples/tree/main/rust) - [Github](https://github.com/pvolnov/py-near) --- # Source: https://docs.near.org/tools/near-cli.md --- id: near-cli title: NEAR CLI description: "Interact with NEAR through the terminal." --- The NEAR [Command Line Interface](https://github.com/near/near-cli-rs) (CLI) is a tool that enables to interact with the NEAR network directly from the shell. Among other things, the NEAR CLI enables you to create and manage accounts, send tokens such as NEAR, FTs and NFTs, deploy smart contracts,call functions on those contracts, and manage access keys. --- ## Installation ```bash npm install -g near-cli-rs@latest ``` ``` $ cargo install near-cli-rs ``` ```bash curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh ``` ```bash irm https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.ps1 | iex ``` --- ## Configuration file The directory with access keys and available connection networks are defined in the configuration file (`near-cli/config.toml`), which is located depending on the operating system in the following places: - macOS: `$HOME/Library/Application Support` (e.g. `/Users/Alice/Library/Application Support`) - Linux: `$XDG_CONFIG_HOME` or `$HOME/.config` (e.g. `/home/alice/.config`) - Windows: `{FOLDERID*RoamingAppData}` (e.g. `C:\Users\Alice\AppData\Roaming`) You can learn more about working with the configuration file [here](https://github.com/near/near-cli-rs/blob/main/docs/GUIDE.en.md#config---manage-connections-in-a-configuration-file). :::tip Custom RPC You can setup a custom [RPC server](../api/rpc/providers) by changing the `rpc_url` parameter in `near-cli` settings: ```bash near config edit-connection testnet --key rpc_url --value https://archival-rpc.testnet.near.org/ ``` ::: --- ## Interactive mode To use the `near-cli` simply run the following in your terminal. ```bash $ near ``` You should then see the following. Use the arrow keys and hit `enter` or simply type out one of the available options to select an option ![](/assets/docs/tools/near-cli-rs.png) :::important We provide examples only of the most used commands. Such commands may have two versions - a **full** one and a **short** one. If you want to explore all options provided by `near-cli` use the interactive mode described above. ::: --- ## Account This option will allow you to manage, control, and retrieve information on your accounts. ### Summary `view-account-summary` - view properties for an account. ```bash export ACCOUNT_ID=bob.testnet near account view-account-summary $ACCOUNT_ID network-config testnet now ``` ```bash export ACCOUNT_ID=bob.testnet near state $ACCOUNT_ID --networkId testnet ```
### Import `import-account` - import existing account (a.k.a. "sign in"). ```bash near account import-account using-web-wallet network-config testnet ``` ```bash near login --networkId testnet ```
### Export `export-account` - export existing account. ```bash export ACCOUNT_ID=bob.testnet near account export-account $ACCOUNT_ID using-web-wallet network-config testnet ```
### Create `create-account` - create a new account. ```bash export ACCOUNT_ID=bob.testnet near account create-account sponsor-by-faucet-service $ACCOUNT_ID autogenerate-new-keypair save-to-keychain network-config testnet create ``` ```bash export ACCOUNT_ID=bob.testnet near create-account $ACCOUNT_ID --useFaucet --networkId testnet ```
### Delete `delete-account` - delete an account. ```bash export ACCOUNT_ID=bob.testnet export BENEFICIARY_ID=alice.testnet near account delete-account $ACCOUNT_ID beneficiary $BENEFICIARY_ID network-config testnet sign-with-keychain send ``` ```bash export ACCOUNT_ID=bob.testnet export BENEFICIARY_ID=alice.testnet near delete-account $ACCOUNT_ID $BENEFICIARY_ID --networkId testnet ``` --- ## Keys Showing, adding and removing account keys. ### List keys `list-keys` - view a list of keys for an account. ```bash export ACCOUNT_ID=bob.testnet near account list-keys $ACCOUNT_ID network-config testnet now ``` ```bash export ACCOUNT_ID=bob.testnet near list-keys $ACCOUNT_ID --networkId testnet ```
### Add key `add-key` - add an access key to an account. ```bash export ACCOUNT_ID=bob.testnet near account add-key $ACCOUNT_ID grant-full-access use-manually-provided-public-key ed25519:CXqAs8c8kZz81josLw82RQsnZXk8CAdUo7jAuN7uSht2 network-config testnet sign-with-keychain send ``` ```bash export ACCOUNT_ID=bob.testnet near add-key $ACCOUNT_ID ed25519:CXqAs8c8kZz81josLw82RQsnZXk8CAdUo7jAuN7uSht2 --networkId testnet ```
### Delete key `delete-keys` - delete an access key from an account. ```bash export ACCOUNT_ID=bob.testnet near account delete-keys $ACCOUNT_ID public-keys ed25519:HdkFZFEPoWfgrrLK3R4t5dWtNoLC8WymBzhCXoP3zrjh network-config testnet sign-with-keychain send ``` ```bash export ACCOUNT_ID=bob.testnet near delete-key $ACCOUNT_ID ed25519:HdkFZFEPoWfgrrLK3R4t5dWtNoLC8WymBzhCXoP3zrjh --networkId testnet ``` --- ## Tokens This will allow you to manage your token assets such as NEAR, FTs and NFTs. ### Send NEAR `send-near` - transfers NEAR to a specified recipient in units of NEAR or yoctoNEAR. ```bash export ACCOUNT_ID=bob.testnet export RECEIVER_ID=alice.testnet near tokens $ACCOUNT_ID send-near $RECEIVER_ID '0.5 NEAR' network-config testnet sign-with-keychain send ``` ```bash export ACCOUNT_ID=bob.testnet export RECEIVER_ID=alice.testnet near send-near $ACCOUNT_ID $RECEIVER_ID 0.5 --networkId testnet ```
### Send FT `send-ft` - transfer Fungible Tokens to a specified user. ```bash export ACCOUNT_ID=bob.testnet export RECEIVER_ID=alice.testnet export FT_CONTRACT_ID=0c97251cd1f630c444dbusdt.testnet near tokens $ACCOUNT_ID send-ft $FT_CONTRACT_ID $RECEIVER_ID amount-ft '1 USDT' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' network-config testnet sign-with-keychain send ```
### Send NFT `send-nft` - transfers NFTs between accounts. ```bash export ACCOUNT_ID=bob.testnet export RECEIVER_ID=alice.testnet export NFT_CONTRACT_ID=nft.examples.testnet near tokens $ACCOUNT_ID send-nft $NFT_CONTRACT_ID $RECEIVER_ID 1 --prepaid-gas '100.0 Tgas' --attached-deposit '1 yoctoNEAR' network-config testnet sign-with-keychain send ```
### View NEAR balance `view-near-balance` - view the balance of NEAR tokens. ```bash export ACCOUNT_ID=bob.testnet near tokens $ACCOUNT_ID view-near-balance network-config testnet now ```
### View FT balance `view-ft-balance` - view the balance of Fungible Tokens. ```bash export ACCOUNT_ID=bob.testnet export FT_CONTRACT_ID=0c97251cd1f630c444dbusdt.testnet near tokens $ACCOUNT_ID view-ft-balance $FT_CONTRACT_ID network-config testnet now ```
### View NFT balance `view-nft-assets` - view the balance of NFT tokens. ```bash export ACCOUNT_ID=bob.testnet export NFT_CONTRACT_ID=nft.examples.testnet near tokens $ACCOUNT_ID view-nft-assets $NFT_CONTRACT_ID network-config testnet now ``` --- ## Contract This option allows you to manage and interact with your smart contracts. ### Call `call-function` - execute function (contract method). ```bash # View method export CONTRACT_ID=nft.examples.testnet near contract call-function as-read-only $CONTRACT_ID nft_tokens json-args '{"from_index": "0", "limit": 2}' network-config testnet now # Call method export ACCOUNT_ID=bob.testnet near contract call-function as-transaction $CONTRACT_ID nft_mint json-args '{"metadata": {"copies": 1, "description": "The Team Goes", "media": "https://bafybeidl4hjbpdr6u6xvlrizwxbrfcyqurzvcnn5xoilmcqbxfbdwrmp5m.ipfs.dweb.link/", "title": "GO TEAM"}, "receiver_id": "bob.testnet", "token_id": "5895"}' prepaid-gas '100.0 Tgas' attached-deposit '0.1 NEAR' sign-as $ACCOUNT_ID network-config testnet sign-with-keychain send ``` ```bash # View method export CONTRACT_ID=nft.examples.testnet near view $CONTRACT_ID nft_tokens '{"from_index": "0", "limit": 2}' --networkId testnet # Call method export ACCOUNT_ID=bob.testnet near call $CONTRACT_ID nft_mint '{"metadata": {"copies": 1, "description": "The Team Goes", "media": "https://bafybeidl4hjbpdr6u6xvlrizwxbrfcyqurzvcnn5xoilmcqbxfbdwrmp5m.ipfs.dweb.link/", "title": "GO TEAM"}, "receiver_id": "bob.testnet", "token_id": "5896"}' --deposit 0.1 --useAccount $ACCOUNT_ID --networkId testnet ```
### Deploy `deploy` - add a new contract code. ```bash export CONTRACT_ID=contract.testnet near contract deploy $CONTRACT_ID use-file ../target/near/contract.wasm without-init-call network-config testnet sign-with-keychain send ``` ```bash export CONTRACT_ID=contract.testnet near deploy $CONTRACT_ID ../target/near/contract.wasm --networkId testnet ```
### Inspect `inspect` - get a list of available function names. ```bash export CONTRACT_ID=nft.examples.testnet near contract view-storage $CONTRACT_ID all as-text network-config testnet now ``` ```bash export CONTRACT_ID=nft.examples.testnet near storage $CONTRACT_ID --finality final --utf8 --networkId testnet ``` --- ## Transaction Operate transactions. ### View status `view-status` - view a transaction status. ```bash near transaction view-status BFrVVtjqD2p1zYX1UCvn4nJpy7zPHpY5cTgQaKCZjBvw network-config testnet ``` ```bash near tx-status BFrVVtjqD2p1zYX1UCvn4nJpy7zPHpY5cTgQaKCZjBvw --networkId testnet ``` --- ## Config Manage the connection parameters inside the `config.toml` file for `near-cli`. This will allow you to change or modify the network connections for your CLI. ### Show connections `show-connections` - show a list of network connections. ```bash near config show-connections ```
### Edit connection `edit-connection` - edit a network connection. ```bash near config edit-connection testnet --key rpc_url --value https://test.rpc.fastnear.com ``` --- :::important We provide examples only of the most used commands. If you want to explore all options provided by `near-cli` use [the interactive mode](#interactive-mode). ::: --- ## Validators You can use the following commands to interact with the blockchain and view validator stats. There are three reports used to monitor validator status: - [Proposals](#proposals) - [Current validators](#current-validators) - [Next validators](#next-validators) :::tip To use these commands, you **must** install the CLI [validator extension](#validator-extension). ::: ### Validator Extension If you want to interact with [NEAR Validators](https://pages.near.org/papers/economics-in-sharded-blockchain/#validators) from command line, you can install the [NEAR Validator CLI Extension](https://github.com/near-cli-rs/near-validator-cli-rs): ```bash npm install -g near-validator ``` ```bash $ cargo install near-validator ``` ```bash curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near-cli-rs/near-validator-cli-rs/releases/latest/download/near-validator-installer.sh | sh ``` ```bash irm https://github.com/near-cli-rs/near-validator-cli-rs/releases/latest/download/near-validator-installer.ps1 | iex ``` ### Proposals A proposal by a validator indicates they would like to enter the validator set, in order for a proposal to be accepted it must meet the minimum seat price. ```sh near-validator proposals network-config mainnet ``` ### Current Validators This shows a list of active validators in the current epoch, the number of blocks produced, number of blocks expected, and online rate. Used to monitor if a validator is having issues. ```sh near-validator validators network-config mainnet now ``` ### Next Validators This shows validators whose proposal was accepted one epoch ago, and that will enter the validator set in the next epoch. ```sh near-validator validators network-config mainnet next ``` ### Staking For validators, there's also an option to stake NEAR tokens without deploying a staking pool smart contract. #### View validator stake To view the validator's stake on the last block, you need to enter in the terminal command line: ```sh near-validator staking view-stake examples.testnet network-config testnet now ``` #### Stake directly without a staking pool To stake the amount you must enter in the terminal command line: ```sh near-validator staking stake-proposal examples.testnet ed25519:AiEo5xepXjY7ChihZJ6AsfoDAaUowhPgvQp997qnFuRP '1500 NEAR' network-config testnet sign-with-keychain send ``` #### Unstake directly without a staking pool To unstake you must enter in the terminal command line: ```sh near-validator staking unstake-proposal examples.testnet ed25519:AiEo5xepXjY7ChihZJ6AsfoDAaUowhPgvQp997qnFuRP network-config testnet sign-with-keychain send ``` --- # Source: https://docs.near.org/web3-apps/tutorials/web-login/near-connector.md --- id: near-connector title: NEAR Connect Tutorial description: "Connect users to NEAR wallets with a secure, sandbox-based connector library" --- The `@hot-labs/near-connect` library provides a secure, zero-dependency wallet connector for NEAR blockchain with a unique sandbox-based architecture. Unlike traditional wallet selectors, it offers a dynamic manifest system that allows wallets to be added and updated without requiring developers to update their dependencies. :::info Working Example For a complete working example with React, check out the [hello-near-connector repository](https://github.com/near-examples/hello-near-connector) which demonstrates all features in action. ::: ![Preview](https://github.com/user-attachments/assets/c4422057-38bb-4cd9-8bd0-568e29f46280) :::tip Why NEAR Connect? - **Secure Execution**: Wallet scripts run in isolated sandboxed iframes for maximum security - **Dynamic Wallets**: Wallets are loaded from a manifest and can be updated without code changes - **Zero Dependencies**: Lightweight library with no external dependencies - **Automatic Detection**: Supports both injected wallets (extensions) and manifest-based wallets ::: --- ## Installation Install the `@hot-labs/near-connect` package along with its required peer dependencies: ```bash npm install @hot-labs/near-connect \ @near-js/providers \ @near-js/utils ``` --- ## Creating the Connector Initialize the `NearConnector` instance in your application. For a complete reference of the `NearConnector` class implementation, see the [source code on GitHub](https://github.com/azbang/hot-connector/blob/main/near-connect/src/NearConnector.ts). ```tsx title="lib/near.ts" // Basic connector for NEAR testnet connector = new NearConnector({ network: "testnet" }); ```
### Selecting Wallets Unlike traditional wallet selectors that bundle wallet code, NEAR Connect uses a **manifest-based approach**: 1. Wallet providers register their integration scripts in a public manifest 2. The connector dynamically loads wallet scripts when users want to connect This architecture eliminates the need to install individual wallet packages and ensures wallet code can be updated independently from your app. ```tsx connector = new NearConnector({ network: "testnet", // or "mainnet" features: { signMessage: true, // Only show wallets that support message signing signTransaction: true, signInWithoutAddKey: true, signAndSendTransaction: true, signAndSendTransactions: true }, }); ```
### Creating an Access Key The connector can request a [Function-Call Key](/protocol/access-keys#function-call-keys) for a specific contract on behalf of the user. This allows your app to interact with the contract without asking the user to sign every transaction. ```tsx title="lib/near.ts" const connector = new NearConnector({ network: "testnet", // or "mainnet" // Optional: Request access key for contract interaction connectWithKey: { contractId: "your-contract.testnet", methodNames: ["method1", "method2"], allowance: "250000000000000", // 0.25 NEAR }, }); ``` --- ## Signing In The connector uses the Observer Pattern (pub/sub), for which we need to do two things: 1. Subscribe to the `signIn` event: ```tsx connector.on("wallet:signIn", async({ wallet, accounts, success }) => { const address = await wallet.getAddress(); console.log(`User signed in: ${address}`); }); ``` 2. Call the `connect` function to open a modal where the user can select a wallet and sign in: ```tsx ``` --- ## Signing Out Similarly, to sign out we need to subscribe to the `signOut` event and call the `disconnect` function: ```tsx // Listen for sign-out connector.on("wallet:signOut", () => { console.log("User signed out"); }); // Disconnect current wallet ``` --- ## Calling Contract Method To call a contract method, first get the connected wallet instance using `connector.wallet()`, then use the wallet's `signAndSendTransaction` method to make a function call: ```tsx // Get the connected wallet const wallet = await connector.wallet(); // Call a change method const result = await wallet.signAndSendTransaction({ receiverId: "hello.near-examples.testnet", actions: [ { type: "FunctionCall", params: { methodName: "set_greeting", args: { greeting: "Hello from NEAR Connect!" }, gas: "30000000000000", // 30 TGas deposit: "0", // No deposit }, }, ], }); console.log("Transaction:", result.transaction.hash); ```
Read-only Methods The `near-connector` does not provide a built-in way to call read-only (view) methods. However, you can use the `@near-js/providers` package to create a JSON-RPC provider and call view methods directly: ```tsx const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); const greeting = await provider.callFunction( "hello.near-examples.testnet", "get_greeting", {} ); ```
--- ## Send Multiple Transactions You can request the user to sign and send multiple transactions in parallel through a single prompt: ```tsx const wallet = await connector.wallet(); const results = await wallet.signAndSendTransactions({ transactions: [ { receiverId: "token.near", actions: [ { type: "FunctionCall", params: { methodName: "ft_transfer", args: { receiver_id: "alice.near", amount: "1000000", }, gas: "30000000000000", deposit: "1", // 1 yoctoNEAR for security }, }, ], }, { receiverId: "nft.near", actions: [ { type: "FunctionCall", params: { methodName: "nft_mint", args: { token_id: "token-1", receiver_id: "alice.near", }, gas: "30000000000000", deposit: "10000000000000000000000", // 0.01 NEAR }, }, ], }, ], }); console.log(`Completed ${results.length} transactions`); ``` --- ### Sign Messages (NEP-413) In NEAR, users can sign messages for authentication purposes without needing to send a transaction: ```tsx const wallet = await connector.wallet(); const signature = await wallet.signMessage({ message: "Please sign this message to authenticate", recipient: "your-app.near", nonce: Buffer.from(crypto.randomUUID()), }); console.log("Signature:", signature.signature); console.log("Public Key:", signature.publicKey); // Verify the signature on your backend ``` --- ## React Integration For React applications, the `near-connector` can be easily integrated using a custom hook. Check out our example [`useNear` hook](https://github.com/near-examples/hello-near-connector/blob/main/src/hooks/useNear.jsx) which handles: - Connector initialization - Auto-reconnect on page load - Event listener management and cleanup - Wallet state synchronization - Error handling --- # Source: https://docs.near.org/protocol/data-flow/near-data-flow.md --- title: NEAR Data Flow sidebar_label: NEAR Data Flow description: "Learn how data flows in NEAR Protocol, including transactions, receipts, shards, and cross-shard communication." --- NEAR Protocol blockchain data flow might be a bit tricky at a glance. But it is pretty straightforward and follows well-defined rules. In this article, we are going to have a closer look at how the data flows in NEAR Protocol blockchain. :::info Data flow In this video we give a quick overview on the main concept of how the data flows across the NEAR Protocol blockchain. ::: In fact, any blockchain flow can be represented as an infinite timeline that has a start but has no end. ![Blocks Timeline](/assets/docs/protocol/data-flow/01-timeline.png) And a block appears on that timeline with some interval. Each of these blocks holds the information about the previous one, thus, creating a *chain of blocks*. NEAR Protocol has a sharded nature, meaning that more than one parallel network, called a `Shard`, can be live at any moment. And each Shard produces a chunk of a block at a given interval. A Block in NEAR Blockchain is a collection of the block chunks from all shards. Block chunk is shortened to `Chunk` in NEAR Protocol documentation. Returning to the data flow itself. The best we can do is to imagine tracks like we could see in audio/video editing apps. Each Shard has its own set of tracks. The top track is for Chunks. They appear no matter what at a given interval, for NEAR Blockchain the interval is about one second. Chunks are being produced even if nothing is happening on the blockchain. ![Timeline as tracks](/assets/docs/protocol/data-flow/02-tracks.png) But what do we mean by saying something is happening? We mean that something has triggered some changes in the blockchain. And the most well-known method to trigger any changes is to send a `Transaction` to the blockchain with instructions of what we want to change and who has requested those changes. A transaction needs to be constructed, signed and sent to the blockchain. Once it is executed we expect to have a result - `ExecutionOutcome`. Oh, that is simple. But it’s not really accurate for NEAR Blockchain. ![Transaction execution](/assets/docs/protocol/data-flow/03-tx-outcome-receipt.png) In the beginning there is a `Transaction`, it contains instructions we want to be executed on the blockchain. Transaction is sent to NEAR Blockchain. And yes, it is executed there immediately BUT the immediate result of the transaction execution is always just *an acknowledgment that it will be executed on the chain;* this internal execution request is known as `Receipt`. You can think of the `Receipt` as an internal transaction that exists to pass information across shards. Let’s get back to the tracks to look at the example. Assuming we have two accounts living on different `Shards` **alice.near** and **bob.near**. **alice.near** creates a `Transaction` to send a few tokens to **bob.near**. The `Transaction` is immediately executed and the `ExecutionOutcome` for the `Transaction` is always a `Receipt`. But this `Receipt` cannot be executed here, because **bob.near** doesn’t live on the same Shard as **alice.near**, so **the Receipt must be executed on the receiver’s Shard**. So the Receipt is moving to the Shard where **bob.near** belongs to. On the destination Shard the Receipt is executed and the process is considered as done. :::info The explanation here is simplified Please refer to the [Token transfer](token-transfer-flow.md) flow article ::: So the final scheme will look like: ![Complete scheme of sending tokens from an account from one Shard to an account on another](/assets/docs/protocol/data-flow/04-send-nears-flow.png) ## Summary We have learnt the main principles of how data is flowing in NEAR Protocol. We have found out that `Transaction` execute and the `ExecutionOutcome` of the Transaction is always a `Receipt`. Now we know that `Receipt` is a main internal asset for NEAR Protocol blockchain and it has a power of traveling between `Shards`. We learnt the NEAR Data flow on a simple example. Of course in real life with more complex transactions that involve cross-contract calls, there will be more Receipts and ExecutionOutcomes. We hope this article is useful and you will be able to build your dApps and indexers easily with the knowledge of how data is flowing in NEAR Protocol. --- # Source: https://docs.near.org/tutorials/examples/near-drop.md --- id: near-drop title: Near Drop description: "Learn how NEAR Drop enables token drops (NEAR, FT, NFT) claimable via private keys." --- NEAR Drop is a smart contract that allows users to create token drops ($NEAR, Fungible and Non-Fungible Tokens), and link them to specific private keys. Whoever has the private key can claim the drop into an existing account, or ask the contract to create a new one for them. Particularly, it shows: 1. How to create a token drops (NEAR, FT and NFT) 2. How to leverage Function Call keys for enabling amazing UX :::tip This example showcases a simplified version of the contract that both [Keypom](https://github.com/keypom/keypom) and the [Token Drop Utility](https://docs.near.org/toolbox?tab=linkdrops) use to distribute tokens to users ::: --- ## Contract Overview The contract exposes 3 methods to create drops of NEAR tokens, FT, and NFT. To claim the tokens, the contract exposes two methods, one to claim in an existing account, and another that will create a new account and claim the tokens into it. This contract leverages NEAR unique feature of [FunctionCall keys](../../protocol/access-keys.md), which allows the contract to create new accounts and claim tokens on behalf of the user. Imagine Alice want to drop some NEAR to Bob: 1. Alice will call `create_near_drop` passing some NEAR amount, and a **Public** Access Key 2. The Contract will check if Alice attached enough tokens and create the drop 3. The Contract will add the `PublicKey` as a `FunctionCall Key` to itself, that **only allow to call the claim methods** 4. Alice will give the `Private Key` to Bob 5. Bob will use the Key to sign a transaction calling the `claim_for` method 6. The Contract will check if the key is linked to a drop, and if it is, it will send the drop It is important to notice that, in step (5), Bob will be using the Contract's account to sign the transaction, and not his own account. Remember that in step (3) the contract added the key to itself, meaning that anyone with the key can call the claim methods in the name of the contract.
Contract's interface #### `create_near_drop(public_keys, amount_per_drop)` Creates `#public_keys` drops, each with `amount_per_drop` NEAR tokens on them #### `create_ft_drop(public_keys, ft_contract, amount_per_drop)` Creates `#public_keys` drops, each with `amount_per_drop` FT tokens, corresponding to the `ft_contract` #### `create_nft_drop(public_key, nft_contract)` Creates a drop with an NFT token, which will come from the `nft_contract` #### `claim_for(account_id)` Claims a drop, which will be sent to the existing `account_id` #### `create_account_and_claim(account_id)` Creates the `account_id`, and then drops the tokens into it
--- ## Contract's State We can see in the contract's state that the contract keeps track of different `PublicKeys`, and links them to a specific `DropId`, which is simply an identifier for a `Drop` (see below). - `top_level_account`: The account that will be used to create new accounts, generally it will be `testnet` or `mainnet` - `next_drop_id`: A simple counter used to assign unique identifiers to each drop - `drop_id_by_key`: A `Map` between `PublicKey` and `DropId`, which allows the contract to know what drops are claimable by a given key - `drop_by_id`: A simple `Map` that links each `DropId` with the actual `Drop` data. ``` #[derive(PanicOnDefault)] #[near(contract_state)] pub struct Contract { pub top_level_account: AccountId, pub next_drop_id: DropId, pub drop_by_id: LookupMap, pub drop_id_by_key: LookupMap, } ``` --- ## Drop Types There are 3 types of drops, which differ in what the user will receive when they claims the corresponding drop - NEAR, fungible tokens (FTs) or non-fungible tokens (NFTs). ``` // This Drop enum stores drop details such as funder, amount to drop or token id, etc. #[derive(Clone, Debug, BorshDeserialize, BorshSerialize)] #[near(serializers = [json])] #[borsh(crate = "near_sdk::borsh")] pub enum Drop { NEAR(NearDrop), FT(FTDrop), NFT(NFTDrop), } ``` ``` #[near(serializers = [json])] #[borsh(crate = "near_sdk::borsh")] pub struct NearDrop { funder: AccountId, // An account which created the drop and funded it amount: NearToken, // Reflects how much NEAR tokens will be transfer to claiming user counter: u32, // Reflects how much times the drop can be claimed } impl Dropper for NearDrop { ``` ``` #[near(serializers = [json])] #[borsh(crate = "near_sdk::borsh")] pub struct FTDrop { funder: AccountId, // Account which created the drop and funded it amount: NearToken, // Reflects how much fungible tokens will be transfer to claiming user ft_contract: AccountId, // Contract of fungible tokens which will be transfer to claiming user counter: u32, // Reflects how much times the drop can be claimed funded: bool, // Reflects if the drop is funded } ``` ``` #[near(serializers = [json])] #[borsh(crate = "near_sdk::borsh")] pub struct NFTDrop { funder: AccountId, // Account which created the drop and funded it token_id: String, // Id of token which will be transfer to claiming user nft_contract: AccountId, // Contract of non-fungible token which will be transfer to claiming user } impl Dropper for NFTDrop { ``` :::info Notice that in this example implementation users cannot mix drops. This is, you can either drop NEAR tokens, or FT, or NFTs, but not a mixture of them (i.e. you cannot drop 1 NEAR token and 1 FT token in the same drop) ::: --- ## Create a drop All `create` start by checking that the user deposited enough funds to create the drop, and then proceed to add the access keys to the contract's account as [FunctionCall Keys](../../protocol/access-keys.md). ``` #[payable] pub fn create_near_drop( &mut self, public_keys: Vec, amount_per_drop: NearToken, ) -> DropId { // check that the access keys are not already used // TODO: add test for that case for public_key in public_keys.iter() { assert!( self.drop_id_by_key.get(public_key).is_none(), "Public key is already used for a drop" ); } let num_of_keys = public_keys.len().try_into().unwrap(); let drop = near_drop::create(amount_per_drop, num_of_keys); let drop_id = self.save_drop(drop); self.save_drop_id_by_keys(&public_keys, drop_id); drop_id } ``` ``` pub fn create(amount_per_drop: NearToken, num_of_keys: u32) -> Drop { let funder = env::predecessor_account_id(); let attached_deposit = env::attached_deposit(); let required_deposit = // required_storage_drop + (required_deposit_per_key * num_of_keys) required_storage_drop(num_of_keys) .saturating_add( required_deposit_per_key(amount_per_drop) .saturating_mul(num_of_keys as u128), ); assert!( attached_deposit >= required_deposit, "Please attach at least {required_deposit}" ); let extra_deposit = attached_deposit.saturating_sub(required_deposit); if extra_deposit.gt(&NearToken::from_yoctonear(0)) { // refund the user, we don't need that money Promise::new(env::predecessor_account_id()).transfer(extra_deposit); } assert!( amount_per_drop.ge(&NearToken::from_yoctonear(1)), "Amount per drop should be at least 1 yN" ); Drop::NEAR(NearDrop { funder, amount: amount_per_drop, counter: num_of_keys, }) } ``` ``` #[payable] pub fn create_ft_drop( &mut self, public_keys: Vec, ft_contract: AccountId, amount_per_drop: NearToken, ) -> DropId { // check that the access keys are not already used for public_key in public_keys.iter() { assert!( self.drop_id_by_key.get(public_key).is_none(), "Public key is already used for a drop" ); } let num_of_keys = public_keys.len().try_into().unwrap(); let drop = ft_drop::create(ft_contract, amount_per_drop, num_of_keys); let drop_id = self.save_drop(drop); self.save_drop_id_by_keys(&public_keys, drop_id); drop_id } ``` ``` pub fn create(ft_contract: AccountId, amount_per_drop: NearToken, num_of_keys: u32) -> Drop { let funder = env::predecessor_account_id(); let attached_deposit = env::attached_deposit(); let required_deposit = // required_storage_drop + (required_deposit_per_key * num_of_keys) required_storage_drop(num_of_keys) .saturating_add( required_deposit_per_key() .saturating_mul(num_of_keys as u128), ); assert!( attached_deposit >= required_deposit, "Please attach at least {required_deposit}" ); let extra_deposit = attached_deposit.saturating_sub(required_deposit); if extra_deposit.gt(&NearToken::from_yoctonear(0)) { // refund the user, we don't need that money Promise::new(env::predecessor_account_id()).transfer(extra_deposit); } assert!( amount_per_drop.ge(&NearToken::from_yoctonear(1)), "Amount per drop cannot be 0" ); Drop::FT(FTDrop { funder, ft_contract, amount: amount_per_drop, counter: num_of_keys, funded: false, }) } ``` ``` #[payable] pub fn create_nft_drop(&mut self, public_key: PublicKey, nft_contract: AccountId) -> DropId { assert!( self.drop_id_by_key.get(&public_key).is_none(), "Public key is already used for a drop" ); let drop = nft_drop::create(nft_contract); let drop_id = self.save_drop(drop); self.save_drop_id_by_key(public_key, drop_id); drop_id } ``` ``` pub fn create(nft_contract: AccountId) -> Drop { let funder = env::predecessor_account_id(); let attached_deposit = env::attached_deposit(); let required_deposit = // required_storage_drop + (required_deposit_per_key * num_of_keys) required_storage_drop() .saturating_add( required_deposit_per_key() ); assert!( attached_deposit >= required_deposit, "Please attach at least {required_deposit}" ); let extra_deposit = attached_deposit.saturating_sub(required_deposit); if extra_deposit.gt(&NearToken::from_yoctonear(0)) { // refund the user, we don't need that money Promise::new(env::predecessor_account_id()).transfer(extra_deposit); } Drop::NFT(NFTDrop { funder, nft_contract, token_id: "".to_string(), }) } ```
### Storage Costs While we will not go into the details of how the storage costs are calculated, it is important to know what is being taken into account: 1. The cost of storing each Drop, which will include storing all bytes associated with the `Drop` struct 2. The cost of storing each `PublicKey -> DropId` relation in the maps 3. Cost of storing each `PublicKey` in the account Notice that (3) is not the cost of storing the byte representation of the `PublicKey` on the state, but the cost of adding the key to the contract's account as a FunctionCall key. --- ## Claim a drop In order to claim drop, a user needs to sign a transaction using the `Private Key`, which is the counterpart of the `Public Key` that was added to the contract. All `Drops` have a `counter` which decreases by 1 each time a drop is claimed. This way, when all drops are claimed (`counter` == 0), we can remove all information from the Drop. There are two ways to claim a drop: claim for an existing account and claim for a new account. The main difference between them is that the first one will send the tokens to an existing account, while the second one will create a new account and send the tokens to it.
``` #[private] pub fn claim_for(&mut self, account_id: AccountId) -> Promise { self.internal_claim(account_id, false) } ``` ``` fn internal_claim(&mut self, account_id: AccountId, account_created: bool) -> Promise { let public_key = env::signer_account_pk(); // get the id for the public_key let drop_id = self .drop_id_by_key .remove(&public_key) .expect("No drop for public key"); let drop = self .drop_by_id .remove(&drop_id) .expect("No drop information for such drop_id"); let counter = drop.get_counter().unwrap_or(1); let updated_counter = counter - 1; let mut drop_deleted = true; if updated_counter > 0 { let mut updated_drop = drop.clone(); let _ = updated_drop.set_counter(updated_counter); self.drop_by_id.insert(drop_id.clone(), updated_drop); drop_deleted = false; } drop.promise_for_claiming(account_id) .then(drop.promise_to_resolve_claim(account_created, drop_deleted)) } } ``` ``` #[private] pub fn create_account_and_claim(&mut self, account_id: AccountId) -> Promise { let public_key = env::signer_account_pk(); if let None = self.drop_id_by_key.get(&public_key) { panic!("No drop for public key") } let create_args = json!({ "new_account_id": account_id, "new_public_key": public_key }) .to_string() .into_bytes() .to_vec(); Promise::new(self.top_level_account.clone()) .function_call( "create_account".to_string(), create_args, CREATE_ACCOUNT_FEE, GAS_FOR_CREATE_ACCOUNT, ) .then( Self::ext(env::current_account_id()) .with_static_gas(CREATE_CALLBACK_GAS) .resolve_account_create(account_id), ) } ``` ``` #[private] pub fn resolve_account_create( &mut self, account_id: AccountId, #[callback_result] created: Result, ) -> Promise { // The first step of creating an account has finished if let Err(_) = created { panic!("Creating account failed") } // Creating the account was successful, we can continue with the claim self.internal_claim(account_id, true) } ``` ``` fn internal_claim(&mut self, account_id: AccountId, account_created: bool) -> Promise { let public_key = env::signer_account_pk(); // get the id for the public_key let drop_id = self .drop_id_by_key .remove(&public_key) .expect("No drop for public key"); let drop = self .drop_by_id .remove(&drop_id) .expect("No drop information for such drop_id"); let counter = drop.get_counter().unwrap_or(1); let updated_counter = counter - 1; let mut drop_deleted = true; if updated_counter > 0 { let mut updated_drop = drop.clone(); let _ = updated_drop.set_counter(updated_counter); self.drop_by_id.insert(drop_id.clone(), updated_drop); drop_deleted = false; } drop.promise_for_claiming(account_id) .then(drop.promise_to_resolve_claim(account_created, drop_deleted)) } } ``` --- ### Testing the Contract The contract readily includes a sandbox testing to validate its functionality. To execute the tests, run the following command: ```bash 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 ``` Then build and deploy the contract: ```bash cargo near build cargo near deploy with-init-call new json-args '{"top_level_account": "testnet"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send ``` --- ### CLI: Interacting with the Contract To interact with the contract through the console, you can use the following commands: ```bash # create a NEAR drop near call create_near_drop '{"public_keys": ["ed25519:AvBVZDQrg8pCpEDFUpgeLYLRGUW8s5h57NGhb1Tc4H5q", "ed25519:4FMNvbvU4epP3HL9mRRefsJ2tMECvNLfAYDa9h8eUEa4"], "amount_per_drop": "10000000000000000000000"}' --useAccount --deposit 1 --gas 100000000000000 # create a FT drop near call create_ft_drop '{"public_keys": ["ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "ed25519:5oN7Yk7FKQMKpuP4aroWgNoFfVDLnY3zmRnqYk9fuEvR"], "amount_per_drop": "1", "ft_contract": ""}' --useAccount --gas 100000000000000 # create a NFT drop near call create_nft_drop '{"public_key": "ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "nft_contract": ""}' --useAccount --gas 100000000000000 # claim to an existing account # see the full version # claim to a new account # see the full version ``` ```bash # create a NEAR drop near contract call-function as-transaction create_near_drop json-args '{"public_keys": ["ed25519:AvBVZDQrg8pCpEDFUpgeLYLRGUW8s5h57NGhb1Tc4H5q", "ed25519:4FMNvbvU4epP3HL9mRRefsJ2tMECvNLfAYDa9h8eUEa4"], "amount_per_drop": "10000000000000000000000"}' prepaid-gas '100.0 Tgas' attached-deposit '1 NEAR' sign-as network-config testnet sign-with-keychain send # create a FT drop near contract call-function as-transaction create_ft_drop json-args '{"public_keys": ["ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "ed25519:5oN7Yk7FKQMKpuP4aroWgNoFfVDLnY3zmRnqYk9fuEvR"], "amount_per_drop": "1", "ft_contract": ""}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send # create a NFT drop near contract call-function as-transaction create_nft_drop json-args '{"public_key": "ed25519:HcwvxZXSCX341Pe4vo9FLTzoRab9N8MWGZ2isxZjk1b8", "nft_contract": ""}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send # claim to an existing account near contract call-function as-transaction claim_for json-args '{"account_id": ""}' prepaid-gas '30.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-plaintext-private-key --signer-public-key ed25519:AvBVZDQrg8pCpEDFUpgeLYLRGUW8s5h57NGhb1Tc4H5q --signer-private-key ed25519:3yVFxYtyk7ZKEMshioC3BofK8zu2q6Y5hhMKHcV41p5QchFdQRzHYUugsoLtqV3Lj4zURGYnHqMqt7zhZZ2QhdgB send # claim to a new account near contract call-function as-transaction create_account_and_claim json-args '{"account_id": ""}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-plaintext-private-key --signer-public-key ed25519:4FMNvbvU4epP3HL9mRRefsJ2tMECvNLfAYDa9h8eUEa4 --signer-private-key ed25519:2xZcegrZvP52VrhehvApnx4McL85hcSBq1JETJrjuESC6v6TwTcr4VVdzxaCReyMCJvx9V4X1ppv8cFFeQZ6hJzU send ``` :::note Versioning for this article At the time of this writing, this example works with the following versions: - near-cli: `0.17.0` - rustc: `1.82.0` ::: --- # Source: https://docs.near.org/data-infrastructure/near-indexer.md --- id: near-indexer title: What is NEAR Indexer? description: "A framework to handle real-time events on the blockchain" --- As scaling dApps enter NEAR’s mainnet, an issue may arise: how do they quickly and efficiently access state from our deployed smart contracts, and cut out the cruft? Contracts may grow to have complex data structures and querying the network RPC may not be the optimal way to access state data. The [NEAR Indexer](https://github.com/near/nearcore/tree/master/chain/indexer) is a micro-framework specifically designed to handle real-time events on the blockchain, allowing to capture and index streams of blocks in a customized manner. With the NEAR Indexer, developers can perform both high-level data aggregation and low-level introspection of blockchain events. :::tip For those searching to not build their own indexer, the [NEAR Lake Framework](./near-lake-framework.md) provides a simpler way to access blockchain data in real-time ::: --- ## How It Works The NEAR Indexer works by **running a node** and processing blocks as they are added to the blockchain. The framework provides a stream of blocks, allowing developers to subscribe and process these blocks in real-time. :::tip Learn how to run it following the [tutorial](./tutorials/near-indexer.md). ::: --- ## Comparison with [NEAR Lake Framework](./near-lake-framework.md) Comparing to NEAR Lake Framework in terms of latency the NEAR Indexer is significantly faster as it reads data directly from the blockchain the same way as RPC nodes do. Feature | Indexer Framework | Lake Framework ------- | ----------------- | -------------- Allows to follow the blocks and transactions in the NEAR Protocol | **Yes** | **Yes**
(but only mainnet and testnet networks) Decentralized | **Yes** | No
(Pagoda Inc dumps the blocks to AWS S3) Reaction time (end-to-end) | minimum 3.8s (estimated average 5-7s) | [minimum 3.9s (estimated average 6-8s)](./near-lake-framework.md#latency) Reaction time (framework overhead only) | 0.1s | 0.2-2.2s Estimated cost of infrastructure | [$500+/mo](https://near-nodes.io/rpc/hardware-rpc) | [**$20/mo**](./near-lake-framework.md#cost) Ease of maintenance | Advanced
(need to follow every nearcore upgrade, and sync state) | **Easy**
(deploy once and forget) How long will it take to start? | days (on mainnet/testnet) | **seconds** Ease of local development | Advanced
(localnet is a good option, but testing on testnet/mainnet is too heavy) | **Easy**
(see [tutorials](./tutorials/near-lake-state-changes-indexer.md)) Programming languages that a custom indexer can be implemented with | Rust only | **Any**
(currently, helper packages are released in [Python](http://pypi.org/project/near-lake-framework), [JavaScript](https://www.npmjs.com/package/near-lake-framework), and [Rust](https://crates.io/crates/near-lake-framework)) --- --- # Source: https://docs.near.org/data-infrastructure/near-lake-framework.md --- id: near-lake-framework title: What is Lake Framework? description: "A library to build your own indexer using the existing Data Lake" --- NEAR Lake Framework is an ecosystem of library companions that read data from the [`NEAR Data Lake`](#data-lake). They allow you to build your own indexer by simply subscribing to the stream of blocks that is being constantly pushed to the [NEAR Lake](#data-lake) *Example of a simple indexer built on top of NEAR Lake Framework* You can create your own logic to process the NEAR Protocol data in the programming languages of your choice: - [Python](http://pypi.org/project/near-lake-framework) - [JavaScript](https://github.com/near/near-lake-framework-js) - [Rust](https://crates.io/crates/near-lake-framework) :::note GitHub repo https://github.com/near/near-lake-framework/ ::: --- ## Data Lake The NEAR Lake is a set of AWS S3 buckets which are constantly receiving new blocks from a [running indexer](https://github.com/aurora-is-near/near-lake-indexer) maintained by [Aurora](https://aurora.dev) Events such as [FT Events](https://github.com/near/NEPs/tree/master/neps/nep-0300.md) and [NFT Events](https://github.com/near/NEPs/tree/master/neps/nep-0256.md) are constantly being written as JSON files in two AWS S3 buckets: - `near-lake-data-testnet` (`eu-central-1` region) - `near-lake-data-mainnet` (`eu-central-1` region) Both buckets are set up the way the requester pays for the access. Anyone can read from these buckets by connecting to them with their own AWS credentials to be charged by Amazon. --- ## Latency Indexers based on the Lake Framework inherit [the latency characteristics of a NEAR Indexer](https://github.com/near/nearcore/tree/master/chain/indexer) **plus an extra 0.1-2.1s latency** of dumping to and reading from AWS S3. :::tip Most of the latency is there due to the finalization delay and both Indexer Framework and Lake Framework add quite a minimum overhead. ::: --- ## Cost Indexers based on NEAR Lake consume 100-500MB of RAM depending on the size of the preloading queue, it does not require any storage, and it can potentially run even on Raspberry PI. Getting the blockchain data from S3 will cost around $30,16 per month as NEAR Lake configured S3 buckets in such a way that **the reader is paying the costs**.
AWS S3 Cost Breakdown Assuming NEAR Protocol produces 1 block every 600ms, on a full day the network can create up to 144000 blocks (86400s / 600ms per block). According to the [Amazon S3 prices](https://aws.amazon.com/s3/pricing/?nc1=h_ls) `list` requests are charged for $0.005 per 1000 requests and `get` is charged for $0.0004 per 1000 requests. Calculations (assuming we are following the tip of the network all the time): ``` 144000 blocks per day * 10 requests for each block / 1000 requests * $0.0004 per 1k requests = $0.576 * 30 days = $17.20 ``` **Note:** 10 requests for each block means we have 9 shards (1 file for common block data and 9 separate files for each shard) And a number of `list` requests we need to perform for 30 days: ``` 144000 blocks per day / 1000 requests * $0.005 per 1k list requests = $0.72 * 30 days = $21.60 $17,20 + $21,60 = $30,16 ```
--- ## Comparison with [NEAR Indexer Framework](near-indexer.md) NEAR Lake Framework is reading data from AWS S3, while the NEAR Indexer is running a full node and reading data from the blockchain directly in real time. Feature | Indexer Framework | Lake Framework ------- | ----------------- | -------------- Allows to follow the blocks and transactions in the NEAR Protocol | **Yes** | **Yes**
(but only mainnet and testnet networks) Decentralized | **Yes** | No
(Pagoda Inc dumps the blocks to AWS S3) Reaction time (end-to-end) | minimum 3.8s (estimated average 5-7s) | [minimum 3.9s (estimated average 6-8s)](#latency) Reaction time (framework overhead only) | 0.1s | 0.2-2.2s Estimated cost of infrastructure | [$500+/mo](https://near-nodes.io/rpc/hardware-rpc) | [**$20/mo**](#cost) Ease of maintenance | Advanced
(need to follow every nearcore upgrade, and sync state) | **Easy**
(deploy once and forget) How long will it take to start? | days (on mainnet/testnet) | **seconds** Ease of local development | Advanced
(localnet is a good option, but testing on testnet/mainnet is too heavy) | **Easy**
(see [tutorials](./tutorials/near-lake-state-changes-indexer.md)) Programming languages that a custom indexer can be implemented with | Rust only | **Any**
(currently, helper packages are released in [Python](http://pypi.org/project/near-lake-framework), [JavaScript](https://www.npmjs.com/package/near-lake-framework), and [Rust](https://crates.io/crates/near-lake-framework)) --- ## Examples & Tutorials - [`near-examples/near-lake-raw-printer`](https://github.com/near-examples/near-lake-raw-printer): simple example of a data printer built on top of NEAR Lake Framework - [`near-examples/near-lake-accounts-watcher`](https://github.com/near-examples/near-lake-accounts-watcher): source code for a video tutorial on how to use the NEAR Lake Framework - [`near-examples/indexer-tx-watcher-example-lake`](https://github.com/near-examples/indexer-tx-watcher-example-lake) indexer example that watches for transaction for specified accounts/contracts build on top of NEAR Lake Framework :::note Tutorials See [Tutorials page](./tutorials/near-lake-state-changes-indexer.md) ::: --- # Source: https://docs.near.org/data-infrastructure/lake-framework/near-lake.md --- id: near-lake sidebar_label: Lake Overview title: NEAR Lake Indexer description: "Learn how NEAR Lake indexes the network" --- NEAR Lake is an indexer built on top of [NEAR Indexer Framework](https://github.com/near/nearcore/tree/master/chain/indexer) to watch the network and store all the event logs such as [FT Events](https://github.com/near/NEPs/tree/master/neps/nep-0300.md) and [NFT Events](https://github.com/near/NEPs/tree/master/neps/nep-0256.md) as JSON files on AWS S3. :::info GitHub repo You can find the Lake Indexer source code in [this GitHub repository](https://github.com/near/near-lake-indexer/). ::: ### How it works :::tip [Pagoda Inc.](https://www.pagoda.co) runs NEAR Lake nodes to store the data in JSON format on AWS S3. There is no need to run your own NEAR Lake unless you have specific reasons to do that. ::: There are AWS S3 buckets created: - `near-lake-data-testnet` (`eu-central-1` region) - `near-lake-data-mainnet` (`eu-central-1` region) All the buckets are set up the way the requester pays for the access. Anyone can read from these buckets by connecting to them with their own AWS credentials to be charged by Amazon. ### Data structure The data structure used by Lake Indexer is the following: ``` / block.json shard_0.json shard_1.json ... shard_N.json ``` `` is a 12-character-long [`u64`](https://doc.rust-lang.org/std/primitive.u64.html) string with leading zeros (e.g "000042839521"). See [this issue for reasoning](https://github.com/near/near-lake/issues/23). `block_json` contains JSON-serialized `BlockView` struct. **NB!** this struct might change in the future, we will announce it `shard_N.json` where N is [`u64`](https://doc.rust-lang.org/std/primitive.u64.html) starting from `0`. Represents the index number of the shard. In order to find out the expected number of shards in the block you can look in `block.json` at `.header.chunks_included` ### How to use it We have created the [NEAR Lake Framework](/data-infrastructure/near-lake-framework) to have a simple straightforward way to create an indexer on top of the data stored by NEAR Lake itself. :::info NEAR Lake Framework You can check the NEAR Lake Framework release announcement on the [NEAR Governance Forum](https://gov.near.org/t/announcement-near-lake-framework-brand-new-word-in-indexer-building-approach/17668). ::: We have prepared this video tutorial with a simple example to give you an overview and some practical ideas. --- # Source: https://docs.near.org/ai/near-mcp.md --- id: near-mcp title: NEAR MCP Server sidebar_label: NEAR MCP Server description: "Equip AI agents with tools for using the NEAR blockchain via Model Context Protocol (MCP)." --- The NEAR MCP Server is an open-source toolkit offers a standardized way for AI applications to integrate with NEAR. It provides [23 tools](https://github.com/nearai/near-mcp/blob/main/TOOLS.md) which enable agents to sign on-chain transactions, manage access keys, exchange tokens, interact with contracts, and more! :::info Heads up... MCP stands for [Model Context Protocol](https://modelcontextprotocol.io), an open-source standard for connecting AI agents with external services Not to be confused with the MPC (Multi-Party Computation) service used by [chain signatures](../chain-abstraction/chain-signatures.md) ::: --- ## Usage Guide Since the NEAR MCP Server needs to handle private keys, it is designed to run locally on your machine or on a trusted remote server. Currently there is **no hosted version** of the NEAR MCP server.
### Quickstart You can either install the MCP server globally and start it, or run it directly using `npx`. ```bash # Install and Run npm install -g @nearai/near-mcp@latest near-mcp run # Without Installing npx @nearai/near-mcp@latest run ``` :::tip By default the MCP server will run a local input/output server, to start a web server instead add the `--port 4000 --remote` flags ```bash npx @nearai/near-mcp@latest run --port 4000 --remote ``` :::
### Local MCP Server Once the NEAR MPC starts locally it starts what is called a [`stdio server`](https://modelcontextprotocol.io/specification/2025-06-18/basic/transports#stdio). To connect your AI agent to the server you will need to use an MCP client library: ``` server_params = StdioServerParameters( command="npx", args=["@nearai/near-mcp@latest", "run"] ) ``` In order to use the NEAR MCP server, you will need to manually parse its tool: ``` def convert_mcp_tools_to_openai(tools_result) -> list[dict]: """Convert MCP tools to OpenAI function format""" return [ { "type": "function", "function": { "name": tool.name, "description": tool.description, "parameters": getattr(tool, 'inputSchema', {"type": "object", "properties": {}}) } } for tool in tools_result.tools ] ``` You can then use the tools as part of your prompt: ``` response = client.chat.completions.create( model=MODEL, messages=message_history, tools=near_mpc_tools, tool_choice="auto", ) ``` :::tip Accounts By default the MCP server will have no accounts loaded, your agent can either import and account using the `system_import_account` tool, or you can specify which keystore to load by setting the `NEAR_KEYSTORE` environment variable ```bash NEAR_KEYSTORE=/path/to/keystore npx @nearai/near-mcp@latest run ``` :::
### Example Prompts The Agent using the MCP server will readily have tool to answer prompts like: - What is the balance of `agency.testnet`? - Add the account `example.testnet` using the private key `ed25519:...` - Transfer 0.1 NEAR from `agency.testnet` to `example.testnet` - Call the `ft_transfer` method on the `usdc.fakes.testnet` contract - What are the public methods of `social.near`? --- ## Integrate With AI Clients You can use the NEAR MCP server with any standard MCP client. Here are instructions for some popular clients: Go to **Settings** > **MCP Servers**, and add a new server with the following details: - **Name**: near-mcp - **Command**: npx - **Arguments**: ["-y", "@nearai/near-mcp@latest", "run"] - **Environment Variables**: (optional) Add any necessary environment variables, e.g., `NEAR_KEYSTORE`. You can now use the NEAR MCP tools in your prompts. ##### 1. Install Claude Code ```bash npm install -g @anthropic-ai/claude-code ``` ##### 2. Add MCP Server to Claude Code ```bash claude mcp add near-mcp npx @nearai/near-mcp@latest run ``` ##### 3. Start Claude Code and Run Tests ``` claude ``` Add to your JSON config: ```json { "mcpServers": { "near-mcp": { "command": "npx", "args": ["-y", "@nearai/near-mcp@latest", "run"], "env": {} } } } ``` --- ## Remote MCP Server Read these [instructions](https://github.com/nearai/near-mcp/blob/main/tee.md) to deploy the MCP server remotely on Phala Cloud. --- ## Contributing Visit NEAR AI's [GitHub repository](https://github.com/nearai/near-mcp) to create issues or submit pull requests. --- # Source: https://docs.near.org/api/rpc/network.md --- id: network title: Network description: "Query node status, network connections, and active validators using the RPC." hide_table_of_contents: true --- The RPC API enables you to query status information for nodes and validators. --- ## Quick Reference | Method | Parameters | Description | | --- | --- | --- | | [`status`](#node-status) | _none_ | Returns general status of a given node and validator set | | [`network_info`](#network-info) | _none_ | Returns current state of node network connections | | [`validators`](#validation-status) | `epoch_id` OR `[null]` | Returns active validators on the network | --- ## Node Status {#node-status} Returns general status of a given node (sync status, nearcore node version, protocol version, etc.), and the current set of validators. - **method**: `status` - **params**: `[]` ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "status", "params": [] } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.status(); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=status \ params:='[]' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "chain_id": "testnet", "genesis_hash": "FWJ9kR6KFWoyMoNjpLXXGHeuiy7tEY6GmoFeCA5yuc6b", "latest_protocol_version": 73, "node_key": null, "node_public_key": "ed25519:DC7DbfZq4dkPqUKaKpWNimgtRBxnD9rja2KcZRs4e3DL", "protocol_version": 73, "rpc_addr": "0.0.0.0:3030", "sync_info": { "earliest_block_hash": "uz2gwgYxpx8dHsjgiPQefbwAhWk41CCvEmHU7ktYE2C", "earliest_block_height": 187251995, "earliest_block_time": "2025-02-10T13:54:22.616904144Z", "epoch_id": "94jeudySZcxGBSVgKXn3xPT3P5iFF6YcnxC43F15QtkQ", "epoch_start_height": 187443633, "latest_block_hash": "EfL8Rc1EH13UxgbJB4skt8xSF8vojNQPcAX1opf6RFab", "latest_block_height": 187456272, "latest_block_time": "2025-02-12T22:10:10.530341781Z", "latest_state_root": "3Vpebx4DuKAYmMjL96XMmLqWYUfuS2raZWoAbxFxeqBm", "syncing": false }, "uptime_sec": 6020117, "validator_account_id": null, "validator_public_key": null, "validators": [ { "account_id": "kiln.pool.f863973.m0", "is_slashed": false }, { "account_id": "node2", "is_slashed": false }, { "account_id": "legends.pool.f863973.m0", "is_slashed": false } ], "version": { "build": "2.4.0-rc.1", "rustc_version": "1.82.0", "version": "2.4.0-rc.1" } }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Network Info {#network-info} Returns the current state of node network connections (active peers, transmitted data, etc.) - **method**: `network_info` - **params**: _none_ ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "network_info", "params": [] } ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=network_info \ params:='[]' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "active_peers": [ { "id": "ed25519:GkDv7nSMS3xcqA45cpMvFmfV1o4fRF6zYo1JRR6mNqg5", "addr": "35.193.24.121:24567", "account_id": null } ], "num_active_peers": 34, "peer_max_count": 40, "sent_bytes_per_sec": 17754754, "received_bytes_per_sec": 492116, "known_producers": [ { "account_id": "node0", "addr": null, "peer_id": "ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Validation Status {#validation-status} Queries active validators on the network returning details and the state of validation on the blockchain. - **method**: `validators` - **params**: - `epoch_id` _OR_ `[null]` **Note:** You can obtain the `epoch_id` from a block that belongs to a specific epoch. If you want to retrieve the current list of validators, pass `null` as the parameter. Additionally, you can query validators for past epochs by providing the `epoch_id` of the desired past epoch. ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "validators", "params": [null] } ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=validators \ params:='[null]' ``` }> ### Example with epoch_id Query validators for a specific past epoch by providing the `epoch_id`. ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "validators", "params": { "epoch_id": "94jeudySZcxGBSVgKXn3xPT3P5iFF6YcnxC43F15QtkQ" } } ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=validators \ params:='{"epoch_id": "94jeudySZcxGBSVgKXn3xPT3P5iFF6YcnxC43F15QtkQ"}' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "current_fishermen": [], "current_proposals": [ { "account_id": "01node.pool.f863973.m0", "public_key": "ed25519:3iNqnvBgxJPXCxu6hNdvJso1PEAc1miAD35KQMBCA3aL", "stake": "14508308808748255650142126217547", "validator_stake_struct_version": "V1" } ], "current_validators": [ { "account_id": "kiln.pool.f863973.m0", "is_slashed": false, "num_expected_blocks": 2622, "num_expected_chunks": 9298, "num_produced_blocks": 2622, "num_produced_chunks": 9288, "public_key": "ed25519:Bq8fe1eUgDRexX2CYDMhMMQBiN13j8vTAVFyTNhEfh1W", "shards": [0], "stake": "92891729926051855086331836750992" } ], "epoch_height": 3358, "epoch_start_height": 187443633, "next_fishermen": [], "next_validators": [ { "account_id": "kiln.pool.f863973.m0", "public_key": "ed25519:Bq8fe1eUgDRexX2CYDMhMMQBiN13j8vTAVFyTNhEfh1W", "shards": [0], "stake": "92921980033422214461941381687070" } ], "prev_epoch_kickout": [] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Error Handling ### Common Error Types | Error Code | Description | Solution | |------------|-------------|----------| | `UnknownEpoch` | Epoch ID not found | Use a valid epoch ID from existing blocks | | `InvalidAccount` | Invalid account format | Use valid account ID format (e.g., `account.near`) | | `RequestTimeout` | Request timed out | Retry the request or use a different RPC endpoint | | `InternalError` | Server-side error | Retry the request after a short delay | | `MethodNotFound` | Invalid method name | Check method spelling and API version | --- ## Best Practices - **Cache validator data**: Validator information changes infrequently, consider caching for several minutes - **Use specific queries**: Request only the data you need to minimize response size --- # Source: https://docs.near.org/protocol/network/networks.md --- id: networks title: NEAR Networks sidebar_label: Networks description: "Explore the different networks available in NEAR" --- NEAR Protocol operates on several networks, each with its own independent validators and unique state. These networks are designed to serve different purposes, from production-ready applications to testing and development environments. ## Overview of NEAR Networks - [`mainnet`](/protocol/network/networks#mainnet) - [`testnet`](/protocol/network/networks#testnet) - [`localnet`](/protocol/network/networks#localnet) ## Mainnet {#mainnet} `mainnet` is for production ready smart contracts and live token transfers. Contracts ready for `mainnet` should have gone through rigorous testing and independent security reviews if necessary. `mainnet` is the only network where state is guaranteed to persist over time _(subject to the typical security guarantees of the network's validation process)_. - Status: `https://rpc.mainnet.near.org/status` - [ [NearBlocks Explorer](https://nearblocks.io) ] - [ [Wallet](https://wallet.near.org) ] - [ [Epoch Sync](https://near-nodes.io/intro/node-epoch-sync) ] ## Testnet {#testnet} `testnet` is a public network and the final testing network for `nearcore` changes before deployment to `mainnet`. `testnet` is intended for testing all aspects of the NEAR platform prior to `mainnet` deployment. From account creation, mock token transfers, development tooling, and smart contract development, the `testnet` environment closely resembles `mainnet` behavior. All `nearcore` changes are deployed as release candidates on first testnet, before the changes are released on `mainnet`. A number of `testnet` validators validate transactions and create new blocks. dApp developers deploy their applications on `testnet` before deploying on `mainnet`. It is important to note that `testnet` has its own transactions and states. - Status: `https://rpc.testnet.near.org/status` - [ [Explorer](https://testnet.nearblocks.io) ] - [ [Wallet](https://testnet.mynearwallet.com/) ] - [ [Epoch Sync](https://near-nodes.io/intro/node-epoch-sync) ] ## Localnet {#localnet} `localnet` is intended for developers who want to work with the NEAR platform independent of the public blockchain. You will need to generate nodes yourself. `localnet` gives you the total control over accounts, economics, and other factors for more advanced use cases (including making changes to `nearcore`). For developers, `localnet` is the right choice if you prefer to avoid leaking information about your work during the development process. More on local development [here](https://near-nodes.io/validator/running-a-node) --- :::tip Got a question? Ask it on StackOverflow! ::: --- # Source: https://docs.near.org/primitives/nft/nft-contract-tools.md --- id: nft-contract-tools title: Create NFT using Contract Tools sidebar_label: Create NFT using Contract Tools description: "Learn how to create a non-fungible token (NFT) using Contract Tools package" --- In this tutorial, you will create a non-fungible token (NFT) using the [NEAR SDK Contract Tools](https://github.com/near/near-sdk-contract-tools) package. This package is a collection of common tools and patterns to simplify smart contract development, including: - Storage fee management - Escrow pattern and derive macro - Owner pattern and derive macro - Pause pattern and derive macro - Role-based access control - Derive macros for [NEP standards](./standard.md) - [NEP-141](https://github.com/near/NEPs/blob/master/neps/nep-0141.md) (fungible token), extension [NEP-148](https://github.com/near/NEPs/blob/master/neps/nep-0148.md) - [NEP-145](https://github.com/near/NEPs/blob/master/neps/nep-0145.md) (storage management), and integrations for the fungible token and non-fungible token standards - [NEP-171](https://github.com/near/NEPs/blob/master/neps/nep-0171.md) (non-fungible token), extensions [NEP-177](https://github.com/near/NEPs/blob/master/neps/nep-0177.md), [NEP-178](https://github.com/near/NEPs/blob/master/neps/nep-0178.md), [NEP-181](https://github.com/near/NEPs/blob/master/neps/nep-0181.md) - [NEP-297](https://github.com/near/NEPs/blob/master/neps/nep-0297.md) (events) --- ## Introduction While one can create a non-fungible token (NFT) contract from scratch using only the `near-sdk` and `near_contract_standards` (e.g. [NFT contract](https://github.com/near-examples/NFT)), a simpler approach is to use `near-sdk-contract-tools`. `near-sdk-contract-tools` allows you to implement the minting/burning logic, access control, and other NFT standards by simply deriving macros on the contract's `struct`, as `OpenZeppelin` does for Ethereum contracts. --- ## Basic NFT Methods To derive basic NFT methods to your smart contract, you need to derive the `NonFungibleToken` macro to the contract's `struct`: ``` #[derive(PanicOnDefault, Owner, NonFungibleToken)] #[non_fungible_token(transfer_hook = "TransferHook")] #[near(contract_state)] pub struct MyNftContract {} ``` This will bring all the basic NFT methods to the contract: - `new` - `contract_source_metadata` - `nft_is_approved` - `nft_metadata` - `nft_supply_for_owner` - `nft_token` - `nft_tokens` - `nft_tokens_for_owner` - `nft_total_supply` - `nft_approve` - `nft_resolve_transfer` - `nft_revoke` - `nft_revoke_all` - `nft_transfer` - `nft_transfer_call` - `storage_balance_bounds` - `storage_balance_of` - `storage_deposit` - `storage_unregister` - `storage_withdraw` To bring basic owner methods to the contract, you also need to derive the `Owner` macro, which adds the following methods: - `own_get_owner` - `own_get_proposed_owner` - `own_accept_owner` - `own_propose_owner` - `own_renounce_owner` --- ## Initialization To initialize the basic NFT contract with a custom owner, metadata, and storage bounds, implement the `new` method: ``` #[near] impl MyNftContract { #[init] pub fn new(owner_id: AccountId, metadata: ContractMetadata) -> Self { let mut contract = Self {}; Owner::init(&mut contract, &owner_id); contract.set_contract_metadata(&metadata); contract.set_storage_balance_bounds(&StorageBalanceBounds { min: NearToken::from_yoctonear(7000000000000000000000), max: Some(NearToken::from_yoctonear(21000000000000000000000)), }); contract } } ``` --- ## Transfer Hook If you want to customize how the token transfer works (i.e., modify the `nft_transfer` method), you need to implement a hook. Hooks are a way to **wrap (inject code before and after)** component functions: ``` pub struct TransferHook; impl Hook> for TransferHook { fn hook( contract: &mut MyNftContract, transfer: &Nep171Transfer<'_>, f: impl FnOnce(&mut MyNftContract) -> R, ) -> R { // Log, check preconditions, save state, etc. log!( "NEP-171 transfer from {} to {} of {} tokens", transfer.sender_id, transfer.receiver_id, transfer.token_id ); let storage_usage_before = env::storage_usage(); let r = f(contract); // execute wrapped function let storage_usage_after = env::storage_usage(); log!( "Storage delta: {}", storage_usage_after - storage_usage_before ); r } } ``` Then derive it to our contract struct: ``` #[derive(PanicOnDefault, Owner, NonFungibleToken)] #[non_fungible_token(transfer_hook = "TransferHook")] #[near(contract_state)] pub struct MyNftContract {} ``` --- ## Minting By default, the NFT standards do not include a minting method. However, you can easily mint tokens for the owner by implementing an `nft_mint` method: ``` #[payable] pub fn nft_mint( &mut self, token_id: TokenId, metadata: TokenMetadata, owner_id: Option, ) { // Check account's storage balance and deposit if necessary let storage_balance_bounds = self.storage_balance_bounds(); log!("Storage balance bounds: {:?}", storage_balance_bounds); let storage_balance = self .storage_balance_of(owner_id.clone().unwrap_or(env::predecessor_account_id())) .unwrap_or_default(); log!("Storage balance: {:?}", storage_balance); if storage_balance.total < storage_balance_bounds.min { // Deposit storage if necessary self.storage_deposit( Some(owner_id.clone().unwrap_or(env::predecessor_account_id())), None, ); } Nep177Controller::mint_with_metadata( self, &token_id, &owner_id.unwrap_or(env::predecessor_account_id()), &metadata, ) .unwrap_or_else(|e| env::panic_str(&e.to_string())); } } ``` :::tip You can modify this method as you need, for example, to allow minting only when the contract is not paused (requires deriving [`Pausable`](https://github.com/near/near-sdk-contract-tools/tree/develop?tab=readme-ov-file#macro-combinations) hook), or to enable minting only to specific accounts with a certain role or from a whitelist with custom limitations. ::: --- ## Burning In the same way that minting is not included in the NFT standards, burning is also not included. However, you can also easily implement it. To allow users to burn their tokens, you can add a `burn` method: ``` #[near] impl MyNftContract { #[payable] pub fn nft_burn(&mut self, token_id: TokenId) { assert_one_yocto(); Nep177Controller::burn_with_metadata(self, &token_id, &env::predecessor_account_id()) .unwrap_or_else(|e| env::panic_str(&e.to_string())); } } ``` --- ## Conclusion Using `near-sdk-contract-tools` is a simple and flexible way to create an NFT contract with minimal boilerplate, which allows you to focus on the business logic. You can further extend this contract with more features like pausing, role-based access control, escrow pattern, and more by deriving corresponding macros from the package. --- # Source: https://docs.near.org/data-infrastructure/tutorials/nft-indexer.md --- sidebar_label: NFT Indexer description: "Learn to build a simple NFT indexer with NEAR Lake Framework." --- This tutorial will guide you through building a simple NFT indexer using the JavaScript version of the [NEAR Lake Framework JS](/data-infrastructure/near-lake-framework). The indexer will listen for `nft_mint` events and print relevant data about newly minted NFTs. The indexer is watching for `nft_mint` [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) and prints some relevant data: - `receiptId` of the `Receipt` where the mint has happened - Marketplace - NFT owner account name - Links to the NFTs on the marketplaces The final source code is available on the GitHub [`near-examples/near-lake-nft-indexer`](https://github.com/near-examples/near-lake-nft-indexer) :::note Source code for the tutorial [`near-examples/near-lake-nft-indexer`](https://github.com/near-examples/near-lake-nft-indexer): source code for this tutorial ::: ## Motivation NEAR Protocol had introduced a nice feature [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md). The Events allow a contract developer to add standardized logs to the `ExecutionOutcomes` thus allowing themselves or other developers to read those logs in more convenient manner via API or indexers. The Events have a field `standard` which aligns with NEPs. In this tutorial we'll be talking about [NEP-171 Non-Fungible Token standard](https://github.com/near/NEPs/discussions/171). In this tutorial our goal is to show you how you can "listen" to the Events contracts emit and how you can benefit from them. As the example we will be building an indexer that watches all the NFTs minted following the [NEP-171 Events](https://github.com/near/NEPs/tree/master/neps/nep-0256.md) standard, assuming we're collectors who don't want to miss a thing. Our indexer should notice every single NFT minted and give us a basic set of data like: in what Receipt it was minted, and show us the link to a marketplace (we'll cover Paras and Bitte/Mintbase in our example). We will use JS version of [NEAR Lake Framework](/data-infrastructure/near-lake-framework) in this tutorial. Though the concept is the same for Rust, but we want to show more people that it's not that complex to build your own indexer. ## Preparation :::danger Credentials Please, ensure you've the credentials set up as described on the [Credentials](./running-near-lake/credentials.md) page. Otherwise you won't be able to get the code working. ::: You will need: - `node` [installed and configured](https://nodejs.org/en/download/) Let's create our project folder ```bash mkdir lake-nft-indexer && cd lake-nft-indexer ``` Let's add the `package.json` ```json title=package.json { "name": "lake-nft-indexer", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "start": "tsc && node index.js" }, "dependencies": { "near-lake-framework": "^1.0.2" }, "devDependencies": { "typescript": "^4.6.4" } } ``` You may have noticed we've added `typescript` as a dev dependency. Let's configure the TypeScript. We'll need to create `tsconfig.json` file for that ```json title=tsconfig.json { "compilerOptions": { "lib": [ "ES2019", "dom" ] } } ``` :::warning ES2019 edition Please, note the `ES2019` edition used. We require it because we are going to use `.flatMap()` and `.flat()` in our code. These methods were introduces in `ES2019`. Though you can use even more recent ::: Let's create empty `index.ts` in the project root and thus finish the preparations. ```bash npm install ``` Now we can start a real work. ## Set up NEAR Lake Framework In the `index.ts` let's import `startStream` function and `types` from `near-lake-framework`: ```ts title=index.ts ``` Add the instantiation of `LakeConfig` below: ```ts title=index.js const lakeConfig: types.LakeConfig = { s3BucketName: "near-lake-data-mainnet", s3RegionName: "eu-central-1", startBlockHeight: 66264389, }; ``` Just a few words on the config, we have set `s3BucketName` for mainnet, default `s3RegionName` and a fresh-ish block height for `startBlockHeight`. You can go to [NEAR Explorer](https://nearblocks.io) and get **the freshest** block height for your setup. Though you can use the same as we do. Now we need to create a callback function that we'll be called to handle `StreamerMessage` our indexer receives. ```ts title=index.ts async function handleStreamerMessage( streamerMessage: types.StreamerMessage ): Promise { } ``` :::info Callback function requirements In `near-lake-framework` JS library the handler have to be presented as a callback function. This function have to: - be asynchronous - accept an argument of type `StreamerMessage` - return nothing (`void`) ::: And an actual start of our indexer in the end of the `index.ts` ```ts title=index.ts (async () => { await startStream(lakeConfig, handleStreamerMessage); })(); ``` The final `index.ts` at this moment should look like the following: ```ts title=index.ts const lakeConfig: types.LakeConfig = { s3BucketName: "near-lake-data-mainnet", s3RegionName: "eu-central-1", startBlockHeight: 66264389, }; async function handleStreamerMessage( streamerMessage: types.StreamerMessage ): Promise { } (async () => { await startStream(lakeConfig, handleStreamerMessage); })(); ``` ## Events and where to catch them First of all let's find out where we can catch the Events. We hope you are familiar with how the [Data Flow in NEAR Blockchain](/protocol/data-flow/near-data-flow), but let's revise our knowledge: - Mint an NFT is an action in an NFT contract (doesn't matter which one) - Actions are located in a `Receipt` - A result of the Receipt execution is `ExecutionOutcome` - `ExecutionOutcome` in turn, catches the logs a contract "prints" - [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) built on top of the logs This leads us to the realization that we can watch only for ExecutionOutcomes and ignore everything else `StreamerMessage` brings us. Also, we need to define an interface to catch the Events. Let's copy the interface definition from the [Events Nomicon page](https://github.com/near/NEPs/tree/master/neps/nep-0297.md#events) and paste it before the `handleStreamerMessage` function. ```ts title=index.ts interface EventLogData { standard: string, version: string, event: string, data?: unknown, }; ``` ## Catching only the data we need Inside the callback function `handleStreamerMessage` we've prepared in the [Preparation](#preparation) section let's start filtering the data we need: ```ts title=index.ts async function handleStreamerMessage( streamerMessage: types.StreamerMessage ): Promise { const relevantOutcomes = streamerMessage .shards .flatMap(shard => shard.receiptExecutionOutcomes) } ``` We have iterated through all the `Shards` and collected the lists of all `ExecutionOutcomes` into a single list (in our case we don't care on which Shard did the mint happen) Now we want to deal only with those `ExecutionOutcomes` that contain logs of Events format. Such logs start with `EVENT_JSON:` according to the [Events docs](https://github.com/near/NEPs/tree/master/neps/nep-0297.md#events). Also, we don't require all the data from ExecutionOutcome, let's handle it: ```ts title=index.ts async function handleStreamerMessage( streamerMessage: types.StreamerMessage ): Promise { const relevantOutcomes = streamerMessage .shards .flatMap(shard => shard.receiptExecutionOutcomes) .map(outcome => ({ receipt: { id: outcome.receipt.receiptId, receiverId: outcome.receipt.receiverId, }, events: outcome.executionOutcome.outcome.logs.map( (log: string): EventLogData => { const [_, probablyEvent] = log.match(/^EVENT_JSON:(.*)$/) ?? [] try { return JSON.parse(probablyEvent) } catch (e) { return } } ) .filter(event => event !== undefined) })) } ``` Let us explain what we are doing here: 1. We are walking through the ExecutionOutcomes 2. We are constructing a list of objects containing `receipt` (it's id and the receiver) and `events` containing the Events 3. In order to collect the Events we are iterating through the ExecutionOutcome's logs trying to parse Event using regular expression. We're returning `undefined` if we fail to parse `EventLogData` 4. Finally once `events` list is collected we're filtering it dropping the `undefined` Fine, so now we have only a list of our objects that contain some Receipt data and the list of successfully parsed `EventLogData`. The goal for our indexer is to return the useful data about a minted NFT that follows NEP-171 standard. We need to drop irrelevant standard Events: ```ts title=index.ts .filter(relevantOutcome => relevantOutcome.events.some( event => event.standard === "nep171" && event.event === "nft_mint" ) ) ``` ## Almost done So far we have collected everything we need corresponding to our requirements. We can print everything in the end of the `handleStreamerMessage`: ```ts title=index.ts relevantOutcomes.length && console.dir(relevantOutcomes, { depth: 10 }) ``` The final look of the `handleStreamerMessage` function: ```ts title=index.ts async function handleStreamerMessage( streamerMessage: types.StreamerMessage ): Promise { const relevantOutcomes = streamerMessage .shards .flatMap(shard => shard.receiptExecutionOutcomes) .map(outcome => ({ receipt: { id: outcome.receipt.receiptId, receiverId: outcome.receipt.receiverId, }, events: outcome.executionOutcome.outcome.logs.map( (log: string): EventLogData => { const [_, probablyEvent] = log.match(/^EVENT_JSON:(.*)$/) ?? [] try { return JSON.parse(probablyEvent) } catch (e) { return } } ) .filter(event => event !== undefined) })) .filter(relevantOutcome => relevantOutcome.events.some( event => event.standard === "nep171" && event.event === "nft_mint" ) ) relevantOutcomes.length && console.dir(relevantOutcomes, { depth: 10 }) } ``` And if we run our indexer we will be catching `nft_mint` event and print the data in the terminal. ```bash npm run start ``` :::note Having troubles running the indexer? Please, check you haven't skipped the [Credentials](./running-near-lake/credentials.md) part :) ::: Not so fast! Remember we were talking about having the links to the marketplaces to see the minted tokens? We're gonna extend our data with links whenever possible. At least we're gonna show you how to deal with the NFTs minted on Paras and Bitte/Mintbase. ## Crafting links to Paras and Mintbase for NFTs minted there At this moment we have an array of objects we've crafted on the fly that exposes receipt, execution status and event logs. We definitely know that all the data we have at this moment are relevant for us, and we want to extend it with the links to that minted NFTs at least for those marketplaces we know. We know and love Paras and Mintbase. ### Paras token URL We did the research for you and here's how the URL to token on Paras is crafting: ``` https://paras.id/token/[1]::[2]/[3] ``` Where: - [1] - Paras contract address (`x.paras.near`) - [2] - First part of the `token_id` (`EventLogData.data` for Paras is an array of objects with `token_ids` key in it. Those IDs represented by numbers with column `:` between them) - [3] - `token_id` itself Example: ``` https://paras.id/token/x.paras.near::387427/387427:373 ``` Let's add the interface for later use somewhere after `interface EventLogData`: ```ts interface ParasEventLogData { owner_id: string, token_ids: string[], }; ``` ### Mintbase token URL And again we did the research for you: ``` https://bitte.ai/thing/[1]:[2] ``` Where: - [1] - `meta_id` (`EventLogData.data` for Mintbase is an array of stringified JSON that contains `meta_id`) - [2] - Store contract account address (basically Receipt's receiver ID) Example: ``` https://bitte.ai/thing/70eES-icwSw9iPIkUluMHOV055pKTTgQgTiXtwy3Xus:vnartistsdao.mintbase1.near ``` Let's add the interface for later use somewhere after `interface EventLogData`: ```ts interface MintbaseEventLogData { owner_id: string, memo: string, } ``` Now it's a perfect time to add another `.map()`, but it might be too much. So let's proceed with a forloop to craft the output data we want to print. ```ts title=index.ts let output = [] for (let relevantOutcome of relevantOutcomes) { let marketplace = "Unknown" let nfts = [] } ``` We're going to print the marketplace name, Receipt id so you would be able to search for it on [NEAR Explorer](https://nearblocks.io) and the list of links to the NFTs along with the owner account name. Let's start crafting the links. Time to say "Hi!" to [Riqi](https://twitter.com/hdriqi) (just because we can): ```ts title=index.ts let output = [] for (let relevantOutcome of relevantOutcomes) { let marketplace = "Unknown" let nfts = [] if (relevantOutcome.receipt.receiverId.endsWith(".paras.near")) { marketplace = "Paras" nfts = relevantOutcome.events.flatMap(event => { return (event.data as ParasEventLogData[]) .map(eventData => ({ owner: eventData.owner_id, links: eventData.token_ids.map( tokenId => `https://paras.id/token/${relevantOutcome.receipt.receiverId}::${tokenId.split(":")[0]}/${tokenId}` ) }) ) }) } ``` A few words about what is going on here. If the Receipt's receiver account name ends with `.paras.near` (e.g. `x.paras.near`) we assume it's from Paras marketplace, so we are changing the corresponding variable. After that we iterate over the Events and its `data` using the `ParasEventLogData` we've defined earlier. Collecting a list of objects with the NFTs owner and NFTs links. Mintbase turn, we hope [Nate](https://twitter.com/nategeier) and his team have migrated to [NEAR Lake Framework](./near-lake-framework.md) already, saying "Hi!" and crafting the link: ```ts title=index.ts } else if (relevantOutcome.receipt.receiverId.match(/\.mintbase\d+\.near$/)) { marketplace = "Mintbase" nfts = relevantOutcome.events.flatMap(event => { return (event.data as MintbaseEventLogData[]) .map(eventData => { const memo = JSON.parse(eventData.memo) return { owner: eventData.owner_id, links: [`https://bitte.ai/thing/${memo["meta_id"]}:${relevantOutcome.receipt.receiverId}`] } }) }) } ``` Almost the same story as with Paras, but a little bit more complex. The nature of Mintbase marketplace is that it's not a single marketplace! Every Mintbase user has their own store and a separate contract. And it looks like those contract addresses follow the same principle they end with `.mintbaseN.near` where `N` is number (e.g. `nate.mintbase1.near`). After we have defined that the ExecutionOutcome receiver is from Mintbase we are doing the same stuff as with Paras: 1. Changing the `marketplace` variable 2. Collecting the list of NFTs with owner and crafted links And if we can't determine the marketplace, we still want to return something, so let's return Events data as is: ```ts title=index.ts } else { nfts = relevantOutcome.events.flatMap(event => event.data) } ``` It's time to push the collected data to the `output` ```ts title=index.ts output.push({ receiptId: relevantOutcome.receipt.id, marketplace, nfts, }) ``` And make it print the output to the terminal: ```ts title=index.ts if (output.length) { console.log(`We caught freshly minted NFTs!`) console.dir(output, { depth: 5 }) } ``` All together: ```ts title=index.ts let output = [] for (let relevantOutcome of relevantOutcomes) { let marketplace = "Unknown" let nfts = [] if (relevantOutcome.receipt.receiverId.endsWith(".paras.near")) { marketplace = "Paras" nfts = relevantOutcome.events.flatMap(event => { return (event.data as ParasEventLogData[]) .map(eventData => ({ owner: eventData.owner_id, links: eventData.token_ids.map( tokenId => `https://paras.id/token/${relevantOutcome.receipt.receiverId}::${tokenId.split(":")[0]}/${tokenId}` ) }) ) }) } else if (relevantOutcome.receipt.receiverId.match(/\.mintbase\d+\.near$/)) { marketplace = "Mintbase" nfts = relevantOutcome.events.flatMap(event => { return (event.data as MintbaseEventLogData[]) .map(eventData => { const memo = JSON.parse(eventData.memo) return { owner: eventData.owner_id, links: [`https://bitte.ai/thing/${memo["meta_id"]}:${relevantOutcome.receipt.receiverId}`] } }) }) } else { nfts = relevantOutcome.events.flatMap(event => event.data) } output.push({ receiptId: relevantOutcome.receipt.id, marketplace, createdOn, nfts, }) } if (output.length) { console.log(`We caught freshly minted NFTs!`) console.dir(output, { depth: 5 }) } ``` OK, how about the date and time of the NFT mint? Let's add to the beginning of the `handleStreamerMessage` function ```ts title=index.ts const createdOn = new Date(streamerMessage.block.header.timestamp / 1000000) ``` Update the `output.push()` expression: ```ts title=index.ts output.push({ receiptId: relevantOutcome.receipt.id, marketplace, createdOn, nfts, }) ``` And not that's it. Run the indexer to watch for NFT minting and never miss a thing. ```bash npm run start ``` :::note Having troubles running the indexer? Please, check you haven't skipped the [Credentials](./running-near-lake/credentials.md) part :) ::: Example output: ``` We caught freshly minted NFTs! [ { receiptId: '2y5XzzL1EEAxgq8EW3es2r1dLLkcecC6pDFHR12osCGk', marketplace: 'Paras', createdOn: 2022-05-24T09:35:57.831Z, nfts: [ { owner: 'dccc.near', links: [ 'https://paras.id/token/x.paras.near::398089/398089:17' ] } ] } ] We caught freshly minted NFTs! [ { receiptId: 'BAVZ92XdbkAPX4DkqW5gjCvrhLX6kGq8nD8HkhQFVt5q', marketplace: 'Mintbase', createdOn: 2022-05-24T09:36:00.411Z, nfts: [ { owner: 'chiming.near', links: [ 'https://bitte.ai/thing/HOTcn6LTo3qTq8bUbB7VwA1GfSDYx2fYOqvP0L_N5Es:vnartistsdao.mintbase1.near' ] } ] } ] ``` ## Conclusion What a ride, yeah? Let's sum up what we have done: - You've learnt about [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) - Now you understand how to follow for the Events - Knowing the fact that as a contract developer you can use Events and emit your own events, now you know how to create an indexer that follows those Events - We've had a closer look at NFT minting process, you can experiment further and find out how to follow `nft_transfer` Events The material from this tutorial can be extrapolated for literally any event that follows the [Events format](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) Not mentioning you have a dedicated indexer to find out about the newest NFTs minted out there and to be the earliest bird to collect them. Let's go hunt doo, doo, doo 🦈 :::note Source code for the tutorial [`near-examples/near-lake-nft-indexer`](https://github.com/near-examples/near-lake-nft-indexer): source code for this tutorial ::: --- # Source: https://docs.near.org/primitives/nft/nft.md --- id: nft title: Using NFTs description: "Learn about NEAR non-fungible tokens (NFT) following NEP-171 and NEP-177 standards - mint, transfer, query, and trade unique digital assets with comprehensive examples." --- Wanting to use Non-Fungible Tokens (NFT) in your dApp? Here you will find all the information you need to get started creating your own tokens, registering users, transferring tokens, and integrating them into your smart contracts. --- ## Deploying a NFT Contract If you want to deploy your own NFT contract, you can create one using our [reference implementation](https://github.com/near-examples/NFT). ```bash near deploy --wasmFile contract.wasm --initFunction new ``` }>
### Global Contract You can deploy a new Non-Fungible Token using our global NFT contract - a pre-deployed [standard NFT contract](https://github.com/near-examples/NFT) that you can reuse. [Global contracts](../../smart-contracts/global-contracts.md) are deployed once and can be reused by any account without incurring high storage costs. ```bash near contract deploy use-global-account-id nft.globals.primitives.testnet \ with-init-call new \ json-args '{"owner_id": "", "metadata": {"spec": "nft-1.0.0", "name": "MY_NFT", "symbol": "NFT2000", "icon": "data:image/svg+xml,%3Csvg xmlns='\''http://www.w3.org/2000/svg'\'' viewBox='\''0 0 288 288'\''%3E%3Cg id='\''l'\'' data-name='\''l'\''%3E%3Cpath d='\''M187.58,79.81l-30.1,44.69a3.2,3.2,0,0,0,4.75,4.2L191.86,103a1.2,1.2,0,0,1,2,.91v80.46a1.2,1.2,0,0,1-2.12.77L102.18,77.93A15.35,15.35,0,0,0,90.47,72.5H87.34A15.34,15.34,0,0,0,72,87.84V201.16A15.34,15.34,0,0,0,87.34,216.5h0a15.35,15.35,0,0,0,13.08-7.31l30.1-44.69a3.2,3.2,0,0,0-4.75-4.2L96.14,186a1.2,1.2,0,0,1-2-.91V104.61a1.2,1.2,0,0,1,2.12-.77l89.55,107.23a15.35,15.35,0,0,0,11.71,5.43h3.13A15.34,15.34,0,0,0,216,201.16V87.84A15.34,15.34,0,0,0,200.66,72.5h0A15.35,15.35,0,0,0,187.58,79.81Z'\''/%3E%3C/g%3E%3C/svg%3E"}}' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain \ send ``` ```bash near contract deploy use-global-hash ivu1e9obVRnMJLSvVPRgtYefUYUS1L3f5eYHjS86zL9 \ with-init-call new \ json-args '{"owner_id": "", "metadata": {"spec": "nft-1.0.0", "name": "MY_NFT", "symbol": "NFT2000", "icon": "data:image/svg+xml,%3Csvg xmlns='\''http://www.w3.org/2000/svg'\'' viewBox='\''0 0 288 288'\''%3E%3Cg id='\''l'\'' data-name='\''l'\''%3E%3Cpath d='\''M187.58,79.81l-30.1,44.69a3.2,3.2,0,0,0,4.75,4.2L191.86,103a1.2,1.2,0,0,1,2,.91v80.46a1.2,1.2,0,0,1-2.12.77L102.18,77.93A15.35,15.35,0,0,0,90.47,72.5H87.34A15.34,15.34,0,0,0,72,87.84V201.16A15.34,15.34,0,0,0,87.34,216.5h0a15.35,15.35,0,0,0,13.08-7.31l30.1-44.69a3.2,3.2,0,0,0-4.75-4.2L96.14,186a1.2,1.2,0,0,1-2-.91V104.61a1.2,1.2,0,0,1,2.12-.77l89.55,107.23a15.35,15.35,0,0,0,11.71,5.43h3.13A15.34,15.34,0,0,0,216,201.16V87.84A15.34,15.34,0,0,0,200.66,72.5h0A15.35,15.35,0,0,0,187.58,79.81Z'\''/%3E%3C/g%3E%3C/svg%3E"}}' \ prepaid-gas '100.0 Tgas' \ attached-deposit '0 NEAR' \ network-config testnet \ sign-with-keychain \ send ``` :::note Deploying by **hash** creates an immutable contract that never changes. Deploying by **account ID** creates an updatable contract that changes when the referenced account's contract is updated. Choose based on whether you want your FT contract to be updatable or permanent. ::: --- ## Minting a NFT To create a new NFT (a.k.a. minting it) you will call the `nft_mint` method passing as arguments the metadata that defines the NFT. Here is a simple form that you can use to mint your own NFT:
Manual Interaction Here is how to directly interact with the factory contract through your application: ```js import { useNearWallet } from "near-connect-hooks"; const CONTRACT_ADDRESS = 'nft.primitives.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: CONTRACT_ADDRESS, method: 'nft_mint', args: { token_id: '1', receiver_id: 'bob.near', token_metadata: { title: 'NFT Primitive Token', description: 'Awesome NFT Primitive Token', media: 'string', // URL to associated media, preferably to decentralized, content-addressed storage }, }, deposit: 10000000000000000000000, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call nft.primitives.near nft_mint '{"token_id": "1", "receiver_id": "bob.near", "token_metadata": {"title": "NFT Primitive Token", "description": "Awesome NFT Primitive Token", "media": "string"}}' --depositYocto 10000000000000000000000, --useAccount bob.near ``` }> ```rust // Validator interface, for cross-contract calls #[ext_contract(ext_nft_contract)] trait ExternalNftContract { fn nft_mint(&self, token_series_id: String, receiver_id: AccountId) -> Promise; } // Implement the contract structure #[near] impl Contract { #[payable] pub fn nft_mint(&mut self, token_series_id: String, receiver_id: AccountId) -> Promise { let promise = ext_nft_contract::ext(self.nft_contract.clone()) .with_static_gas(Gas(30*TGAS)) .with_attached_deposit(env::attached_deposit()) .nft_mint(token_series_id, receiver_id); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(Gas(30*TGAS)) .nft_mint_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn nft_mint_callback(&self, #[callback_result] call_result: Result) -> Option { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting NFT contract"); return None; } // Return the token data let token_id: TokenId = call_result.unwrap(); return Some(token_id); } } ``` :::info See the [metadata standard](https://github.com/near/NEPs/tree/master/neps/nep-0177.md) for the full list of `TokenMetadata` parameters. ::: :::warning Values of gas and deposit might vary depending on which NFT contract you are calling. :::

### Minting Collections Many times people want to create multiple 100 copies of an NFT (this is called a collection). In such cases, what you actually need to do is to mint 100 different NFTs with the same metadata (but different `token-id`). :::tip Notice that [minting in Mintbase](#minting-a-nft) allows you to pass a `num_to_mint` parameter. :::
### Royalties You might have noticed that one of the parameters is a structure called royalties. Royalties enable you to create a list of users that should get paid when the token is sell in a marketplace. For example, if `anna` has `5%` of royalties, each time the NFT is sell, `anna` should get a 5% of the selling price. --- ## Querying NFT data You can query the NFT's information and metadata by calling the `nft_token`. ```js import { useNearWallet } from "near-connect-hooks"; const CONTRACT_ADDRESS = 'nft.primitives.near'; const { viewFunction } = useNearWallet(); const response = await viewFunction({ contractId: CONTRACT_ADDRESS, method: 'nft_token', args: { token_id: '1', }, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application
Example response ```json { "token_id": "1", "owner_id": "bob.near", "metadata": { "title": "string", // ex. "Arch Nemesis: Mail Carrier" or "Parcel #5055" "description": "string", // free-form description "media": "string", // URL to associated media, preferably to decentralized, content-addressed storage "media_hash": "string", // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included. "copies": 1, // number of copies of this set of metadata in existence when token was minted. "issued_at": 1642053411068358156, // When token was issued or minted, Unix epoch in milliseconds "expires_at": 1642053411168358156, // When token expires, Unix epoch in milliseconds "starts_at": 1642053411068358156, // When token starts being valid, Unix epoch in milliseconds "updated_at": 1642053411068358156, // When token was last updated, Unix epoch in milliseconds "extra": "string", // anything extra the NFT wants to store on-chain. Can be stringified JSON. "reference": "string", // URL to an off-chain JSON file with more info. "reference_hash": "string" // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included. } } ```
```bash near view nft.primitives.near nft_token '{"token_id": "1"}' ```
Example response ```json { "token_id": "1", "owner_id": "bob.near", "metadata": { "title": "string", // ex. "Arch Nemesis: Mail Carrier" or "Parcel #5055" "description": "string", // free-form description "media": "string", // URL to associated media, preferably to decentralized, content-addressed storage "media_hash": "string", // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included. "copies": 1, // number of copies of this set of metadata in existence when token was minted. "issued_at": 1642053411068358156, // When token was issued or minted, Unix epoch in milliseconds "expires_at": 1642053411168358156, // When token expires, Unix epoch in milliseconds "starts_at": 1642053411068358156, // When token starts being valid, Unix epoch in milliseconds "updated_at": 1642053411068358156, // When token was last updated, Unix epoch in milliseconds "extra": "string", // anything extra the NFT wants to store on-chain. Can be stringified JSON. "reference": "string", // URL to an off-chain JSON file with more info. "reference_hash": "string" // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included. } } ```
}>
Example response ```json { "token_id": "1", "owner_id": "bob.near", "metadata": { "title": "string", // ex. "Arch Nemesis: Mail Carrier" or "Parcel #5055" "description": "string", // free-form description "media": "string", // URL to associated media, preferably to decentralized, content-addressed storage "media_hash": "string", // Base64-encoded sha256 hash of content referenced by the `media` field. Required if `media` is included. "copies": 1, // number of copies of this set of metadata in existence when token was minted. "issued_at": 1642053411068358156, // When token was issued or minted, Unix epoch in milliseconds "expires_at": 1642053411168358156, // When token expires, Unix epoch in milliseconds "starts_at": 1642053411068358156, // When token starts being valid, Unix epoch in milliseconds "updated_at": 1642053411068358156, // When token was last updated, Unix epoch in milliseconds "extra": "string", // anything extra the NFT wants to store on-chain. Can be stringified JSON. "reference": "string", // URL to an off-chain JSON file with more info. "reference_hash": "string" // Base64-encoded sha256 hash of JSON from reference field. Required if `reference` is included. } } ```
```rust // Validator interface, for cross-contract calls #[ext_contract(ext_nft_contract)] trait ExternalNftContract { fn nft_token(&self, token_id: TokenId) -> Promise; } // Implement the contract structure #[near] impl Contract { pub fn nft_token(&self, token_id: TokenId) -> Promise { let promise = ext_nft_contract::ext(self.nft_contract.clone()) .nft_token(token_id); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .nft_token_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn nft_token_callback(&self, #[callback_result] call_result: Result) -> Option { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting NFT contract"); return None; } // Return the token data let token_data: Token = call_result.unwrap(); return Some(token_data); } } ```
--- ## Transferring a NFT Transferring an NFT can happen in two scenarios: (1) you ask to transfer an NFT, and (2) an [authorized account](#approving-users) asks to transfer the NFT. In both cases, it is necessary to invoke the `nft_transfer` method, indicating the token id, the receiver, and an (optionally) an [approval_id](https://github.com/near/NEPs/tree/master/neps/nep-0178.md). ```js import { useNearWallet } from "near-connect-hooks"; const CONTRACT_ADDRESS = 'nft.primitives.near'; const { callFunction } = useNearWallet(); await callFunction({ contractId: CONTRACT_ADDRESS, method: 'nft_transfer', args: { token_id: '1', receiver_id: 'bob.near', }, deposit: 1, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call nft.primitives.near nft_transfer '{"token_id": "1", "receiver_id": "bob.near"}' --useAccount bob.near --deposit 0.000000000000000000000001 ``` }> :::info Please notice that a contract can only transfer an NFT that they own, or an NFT that they were approved to transfer. ::: ```rust const YOCTO_NEAR: u128 = 1; #[ext_contract(ext_nft_contract)] trait ExternalNftContract { fn nft_transfer(&self, receiver_id: AccountId, token_id: TokenId) -> Promise; } impl Contract { #[payable] pub fn nft_transfer(&mut self, receiver_id: AccountId, token_id: TokenId) -> Promise { let promise = ext_nft_contract::ext(self.nft_contract.clone()) .with_attached_deposit(YOCTO_NEAR) .nft_transfer(receiver_id, token_id); return promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .nft_transfer_callback() ) } #[private] // Public - but only callable by env::current_account_id() pub fn nft_transfer_callback(&self, #[callback_result] call_result: Result<(), PromiseError>) { // Check if the promise succeeded if call_result.is_err() { log!("There was an error contacting NFT contract"); } } } ``` --- ## Attaching NFTs to a Call Natively, only NEAR tokens (Ⓝ) can be attached to a function calls. However, the NFT standard enables to attach a non-fungible tokens in a call by using the NFT-contract as intermediary. This means that, instead of you attaching tokens directly to the call, you ask the NFT-contract to do both a transfer and a function call in your name. ```bash near call nft_transfer_call '{"receiver_id": "", "token_id": "", "msg": ""}' --useAccount --depositYocto 1 ``` }> :::info Optionally, a [`memo` parameter](https://github.com/near/NEPs/tree/master/neps/nep-0171.md#nft-interface) can be passed to provide more information to your contract. :::
### How Does it Work? Assume you want to attach an NFT (🎫) to a call on the receiver contract. The workflow is as follows: 1. You call `nft_transfer_call` in the NFT-contract passing: the receiver, a message, and the token-id of 🎫. 2. The NFT contract transfers the NFT 🎫 to the receiver. 3. The NFT contract calls **`receiver.nft_on_transfer(sender, token-owner, token-id, msg)`**. 4. The NFT contract handles errors in the `nft_resolve_transfer` callback. 5. The NFT contract returns `true` if it succeeded. #### The nft_on_transfer method From the workflow above it follows that the receiver we want to call needs to implement the `nft_on_transfer` method. When executed, such method will know: - Who is sending the NFT, since it is a parameter - Who is the current owner, since it is a parameter - Which NFT was transferred, since it is a parameter. - If there are any parameters encoded as a message The `nft_on_transfer` **must return true** if the NFT has to be **returned to the sender**. --- ## Approving Users You can authorize other users to transfer an NFT you own. This is useful, for example, to enable listing your NFT in a marketplace. In such scenario, you **trust** that the marketplace will only transfer the NFT upon receiving a certain amount of money in exchange. ```bash near call nft_approve '{ "token_id": "", "account_id": "", "msg": "" }' --useAccount --depositYocto 1 ``` }> :::info If the `msg` parameter is included, then a cross-contract call will be made to `.nft_on_approve(msg)`. Which in turn will make a callback to `nft_resolve_transfer` in your NFT contract. ::: --- ## Burn Tokens While the NFT standard does not define a `burn` method, you can simply transfer tokens to an account that no one controls, such as [`0000000000000000000000000000000000000000000000000000000000000000`](https://nearblocks.io/es/address/0000000000000000000000000000000000000000000000000000000000000000) (64 zeros). ```js import { useNearWallet } from "near-connect-hooks"; const { callFunction } = useNearWallet(); await callFunction({ contractId: 'nft.primitives.near', method: 'nft_transfer', args: { token_id: '1', receiver_id: '0000000000000000000000000000000000000000000000000000000000000000', }, deposit: 1, }); ``` Learn more about adding [Near Connect](../../web3-apps/tutorials/wallet-login) to your application ```bash near call nft.primitives.near nft_transfer '{"token_id": "1", "receiver_id": "0000000000000000000000000000000000000000000000000000000000000000"}' --useAccount bob.near --deposit 0.000000000000000000000001 ``` --- ## Tutorials - [NFT Tutorial](/tutorials/nfts-js) _Zero to Hero_ (JavaScript SDK) - a set of tutorials that cover how to create a NFT contract using JavaScript. - [NFT Tutorial](/tutorials/nfts) _Zero to Hero_ (Rust SDK) - a set of tutorials that cover how to create a NFT contract using Rust. --- # Source: https://docs.near.org/tutorials/nfts-js.md --- id: nfts-js title: NFT Zero to Hero JavaScript Edition sidebar_label: Build a NFT Contract from Scratch (JS) description: "Learn NFTs from minting to building a full-featured smart contract in this Zero to Hero series." --- > In this _Zero to Hero_ series, you'll find a set of tutorials that will cover every aspect of a non-fungible token (NFT) smart contract. > You'll start by minting an NFT using a pre-deployed contract and by the end you'll end up building a fully-fledged NFT smart contract that supports every extension. ## Prerequisites To complete these tutorials successfully, you'll need: - [Node.js](/smart-contracts/quickstart?code-tabs=js) - [A NEAR Wallet](https://testnet.mynearwallet.com/create) - [NEAR-CLI](/tools/near-cli#installation) --- ## Overview These are the steps that will bring you from **_Zero_** to **_Hero_** in no time! 💪 | Step | Name | Description | |------|------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| | 1 | [Pre-deployed contract](https://near-examples.github.io/nft-tutorial-js/predeployed-contract) | Mint an NFT without the need to code, create, or deploy a smart contract. | | 2 | [Contract architecture](https://near-examples.github.io/nft-tutorial-js/skeleton) | Learn the basic architecture of the NFT smart contract and compile code. | | 3 | [Minting](https://near-examples.github.io/nft-tutorial-js/minting) | Flesh out the skeleton so the smart contract can mint a non-fungible token. | | 4 | [Upgrade a contract](https://near-examples.github.io/nft-tutorial-js/upgrade-contract) | Discover the process to upgrade an existing smart contract. | | 5 | [Enumeration](https://near-examples.github.io/nft-tutorial-js/enumeration) | Explore enumeration methods that can be used to return the smart contract's states. | | 6 | [Core](https://near-examples.github.io/nft-tutorial-js/core) | Extend the NFT contract using the core standard which allows token transfer | | 7 | [Approvals](https://near-examples.github.io/nft-tutorial-js/approvals) | Expand the contract allowing other accounts to transfer NFTs on your behalf. | | 8 | [Royalty](https://near-examples.github.io/nft-tutorial-js/royalty) | Add NFT royalties allowing for a set percentage to be paid out to the token creator. | | 9 | [Events](https://near-examples.github.io/nft-tutorial-js/events) | in this tutorial you'll explore the events extension, allowing the contract to react on certain events. | | 10 | [Marketplace](https://near-examples.github.io/nft-tutorial-js/marketplace) | Learn about how common marketplaces operate on NEAR and dive into some of the code that allows buying and selling NFTs | --- ## Next steps Ready to start? Jump to the [Pre-deployed Contract](https://near-examples.github.io/nft-tutorial-js/predeployed-contract) tutorial and begin your learning journey! If you already know about non-fungible tokens and smart contracts, feel free to skip and jump directly to the tutorial of your interest. The tutorials have been designed so you can start at any given point! :::info Questions? 👉 Join us on [Discord](https://near.chat/) and let us know in the `#development` channels. 👈 ::: --- # Source: https://docs.near.org/tutorials/nfts.md --- id: nfts title: NFT Zero to Hero sidebar_label: Build a NFT Contract from Scratch description: "Learn how to mint NFTs and build a full NFT contract step by step." --- In this _Zero to Hero_ series, you'll find a set of tutorials that will cover every aspect of a non-fungible token (NFT) smart contract. You'll start by minting an NFT using a pre-deployed contract and by the end you'll end up building a fully-fledged NFT smart contract that supports every extension. --- ## Prerequisites To complete these tutorials successfully, you'll need: - [Rust](https://www.rust-lang.org/tools/install) - [A Testnet wallet](https://testnet.mynearwallet.com/create) - [NEAR-CLI](/tools/near-cli#installation) - [cargo-near](https://github.com/near/cargo-near) :::info New to Rust? If you are new to Rust and want to dive into smart contract development, our [Quick-start guide](../smart-contracts/quickstart.md) is a great place to start ::: --- ## Overview These are the steps that will bring you from **_Zero_** to **_Hero_** in no time! 💪 | Step | Name | Description | |------|---------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------| | 1 | [Pre-deployed contract](https://near-examples.github.io/nft-tutorial/predeployed-contract) | Mint an NFT without the need to code, create, or deploy a smart contract. | | 2 | [Contract architecture](https://near-examples.github.io/nft-tutorial/skeleton) | Learn the basic architecture of the NFT smart contract and compile code. | | 3 | [Minting](https://near-examples.github.io/nft-tutorial/minting) | Flesh out the skeleton so the smart contract can mint a non-fungible token. | | 4 | [Upgrade a contract](https://near-examples.github.io/nft-tutorial/upgrade-contract) | Discover the process to upgrade an existing smart contract. | | 5 | [Enumeration](https://near-examples.github.io/nft-tutorial/enumeration) | Explore enumeration methods that can be used to return the smart contract's states. | | 6 | [Core](https://near-examples.github.io/nft-tutorial/core) | Extend the NFT contract using the core standard which allows token transfer. | | 7 | [Events](https://near-examples.github.io/nft-tutorial/events) | The events extension, allowing the contract to react on certain events. | | 8 | [Approvals](https://near-examples.github.io/nft-tutorial/approvals) | Expand the contract allowing other accounts to transfer NFTs on your behalf. | | 9 | [Royalty](https://near-examples.github.io/nft-tutorial/royalty) | Add NFT royalties allowing for a set percentage to be paid out to the token creator. | | 10 | [Marketplace](https://near-examples.github.io/nft-tutorial/marketplace) | Learn about how common marketplaces operate on NEAR and dive into some of the code that allows buying and selling NFTs. | --- ## Next steps Ready to start? Jump to the [Pre-deployed Contract](https://near-examples.github.io/nft-tutorial/predeployed-contract) tutorial and begin your learning journey! If you already know about non-fungible tokens and smart contracts, feel free to skip and jump directly to the tutorial of your interest. The tutorials have been designed so you can start at any given point! --- # Source: https://docs.near.org/smart-contracts/security/one-yocto.md --- id: one-yocto title: Ensure it is the User (1yⓃ) description: "Learn about the one yocto security pattern in NEAR smart contracts for verifying account ownership and preventing unauthorized access." --- NEAR uses a system of [Access Keys](../../protocol/access-keys.md) to simplify handling accounts.There are basically two type of keys: `Full Access`, that have full control over an account (i.e. can perform all [actions](../anatomy/actions.md)), and`Function Call`, that only have permission to call a specified smart contract's method(s) that _do not_ attach Ⓝ as a deposit. For example, developers can choose to request creating a `Function Call` key when the user logs in, so the application can call specific methods on a smart contract without needing to ask the user to sign every time. While this is very user friendly for most cases, it is important to be careful in scenarios involving transferring of valuable assets like [NFTs](../../primitives/nft/nft.md) or [FTs](../../primitives/ft/ft.md). In such cases, you need to ensure that the person asking for the asset to be transfer is **actually the user**. One direct and inexpensive way to ensure that the user is the one calling is by requiring to attach `1 yⓃ`. In this case, the user will be redirected to the wallet and be asked to accept the transaction. This is because, once again, only the `Full Access` key can be used to send NEAR. Since the `Full Access` key is only in the user's wallet, you can trust that a transaction with `1 yⓃ` was made by the user. --- # Source: https://docs.near.org/primitives/oracles.md --- id: oracles title: Oracles description: "Learn about blockchain oracles on NEAR Protocol, including price feeds, data integration, and using oracle services like NearDefi Price Oracle and Pyth Network." --- [Blockchain Oracles](https://en.wikipedia.org/wiki/Blockchain_oracle) serve as intermediaries that connect smart contracts with external (off-chain) data. `Example:` - **Price Feeds:** Real-time pricing for cryptocurrencies, stocks, or commodities. - **Event Information:** Updates on real-world events like sports results or weather conditions. - **API Access:** Connections to external web services and systems. :::info Oracles, being external third-party services, require careful consideration of their reliability, security, and decentralization to avoid risks such as incorrect data, manipulation, or single points of failure. ::: --- ## Deployed Oracles on NEAR Here is a directory of third-party oracle services deployed on the NEAR blockchain: | Name | Creator | Description | | -------------------------------------------------------------------------------------------------------- | --------------------------------------- | -------------------------------------------------- | | [Price Oracle](#price-oracle-by-neardefi) | [NearDefi](https://github.com/NearDeFi) | Open source oracle for real-time asset pricing | | [Pyth Network Oracle](#pyth-network-oracle) | [Pyth Network](https://pyth.network/) | High-frequency, low-latency oracle for price feeds | | **[[Your Project Here]](https://github.com/near/assets/docs/edit/master/assets/docs/primitives/oracles.md)** | - | - | --- ## Price Oracle by NearDefi - **Creator:** [NearDefi](https://github.com/NearDeFi) - **Codebase Repository:** [NearDefi/price-oracle](https://github.com/NearDeFi/price-oracle) - **Bot for Data Feeds:** [NearDefi/near-price-oracle-bot](https://github.com/NearDeFi/near-price-oracle-bot) - **Deployed Addresses:** - Mainnet: [priceoracle.near](https://nearblocks.io/address/priceoracle.near) - Testnet: [priceoracle.testnet](https://testnet.nearblocks.io/address/priceoracle.testnet) ### Query Assets ```bash near view priceoracle.near get_assets near contract call-function as-read-only priceoracle.near get_assets json-args {} network-config mainnet now ```
Example Response ```bash [ [ 'wrap.near', { reports: [ { oracle_id: 'thorinoracle.near', timestamp: '1669795900809627816', price: { multiplier: '17030', decimals: 28 } }, ... ] } ] ] ```
}>
Example Response ```bash [ [ 'wrap.near', { reports: [ { oracle_id: 'thorinoracle.near', timestamp: '1669795900809627816', price: { multiplier: '17030', decimals: 28 } }, ... ] } ] ] ```
### Get Assets Price ```bash near view priceoracle.near get_price_data ```
Example response ```bash { timestamp: '1706631861981947371', recency_duration_sec: 90, prices: [ { asset_id: 'wrap.near', price: { multiplier: '30702', decimals: 28 } }, { asset_id: 'aurora', price: { multiplier: '235662', decimals: 20 } }, { asset_id: 'meta-pool.near', price: { multiplier: '38770', decimals: 28 } }, { asset_id: 'linear-protocol.near', price: { multiplier: '36432', decimals: 28 } }, ```
}>
Example response ```bash { timestamp: '1706631861981947371', recency_duration_sec: 90, prices: [ { asset_id: 'wrap.near', price: { multiplier: '30702', decimals: 28 } }, { asset_id: 'aurora', price: { multiplier: '235662', decimals: 20 } }, { asset_id: 'meta-pool.near', price: { multiplier: '38770', decimals: 28 } }, { asset_id: 'linear-protocol.near', price: { multiplier: '36432', decimals: 28 } }, ```
:::tip For USD values, divide the `multiplier` by `10^4`. ::: --- ## Pyth Network Oracle - **Creator:** [Pyth Network](https://pyth.network) - **Official Documentation:** [Pyth NEAR Docs](https://docs.pyth.network/price-feeds/use-real-time-data/near) - **Codebase Repository:** [pyth-network/pyth-crosschain](https://github.com/pyth-network/pyth-crosschain/tree/main/target_chains/near) - **Deployed Addresses:** - **Mainnet:** [pyth-oracle.near](https://nearblocks.io/address/pyth-oracle.near) - **Testnet:** [pyth-oracle.testnet](https://testnet.nearblocks.io/address/pyth-oracle.testnet) --- ## Using Pyth Network Oracle > Pyth Network's NEAR smart contract has two core methods to update & get price feeds of your choice. 1. [`update_price_feeds`](#update_price_feeds) _(updates Pyth smart contract with the price feed you provide)_ - args: `data` - type: `object` - example: `{ "data": "504e41...' }` 2. [`get_price`](#get_price) (fetches the most recent price stored in the contract)\_ - args: `price_identifier` - type: `object` - example: `{ price_identifier: 'f9c0172ba10dfa8...' }` :::info For a complete list of endpoints to interact with, see [Pyth's `receiver` contract](https://github.com/pyth-network/pyth-crosschain/blob/main/target_chains/near/receiver/src/ext.rs). ::: ### Getting Started To get started with Pyth oracle you will need to gather the following information which differ between networks: - Price ID(s) - HermesAPI Endpoint - Smart contract address | Network | Price Feed IDs | Hermes API Address | Contract Address | | --------- | ------------------------------------------------------------------------------------------------ | -------------------------- | -------------------------------------------------------------------------------- | | `testnet` | [NEAR `testnet` Price Feed IDs](https://www.pyth.network/developers/price-feed-ids#near-testnet) | `hermes-beta.pyth.network` | [pyth-oracle.testnet](https://testnet.nearblocks.io/address/pyth-oracle.testnet) | | `mainnet` | [NEAR `mainnet` Price Feed IDs](https://www.pyth.network/developers/price-feed-ids#near-mainnet) | `hermes.pyth.network` | [pyth-oracle.near](https://nearblocks.io/address/pyth-oracle.near) | :::info When using Price Feed IDs, you will need to remove the `0x` prefix. `Price Feed ID Example (testnet):` ``` 'f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b', // BTC/USD price id 'ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6', // ETH/USD price id ]; ``` ::: --- ### `update_price_feeds` > Updates the Pyth Oracle contract data with the price feed you provide. - args: `data` _(off-chain hex-encoded price feed)_ - type: `object` - example: `{ "data": "504e41...' }` Update the Pyth Oracle contract with new price feed data in two main steps: 1. [Fetch off-chain price feed](#1-fetching-off-chain-price-feed) 2. [Update Pyth Oracle contract with off-chain price feed](#2-update-pyth-oracle-contract-price-feed) #### 1) Fetching off-chain price feed You can obtain an off-chain price feed using Pyth's [Hermes API](https://hermes-beta.pyth.network/docs/). To use these endpoints, you will need to provide a Price Feed ID and ensure you are targeting the correct network. See [Getting Started](#getting-started) for more information. Here is a node.js example of fetching the latest price feed using `/v2/updates/price/latest` endpoint: ``` const axios = require('axios'); // There are separate endpoints for testnet and mainnet const HERMES_TESTNET_URL = 'https://hermes-beta.pyth.network'; const HERMES_MAINNET_URL = 'https://hermes.pyth.network'; async function getHermesPriceData(priceId, network) { try { let url; network === 'testnet' ? (url = HERMES_TESTNET_URL) : (url = HERMES_MAINNET_URL); // Fetch the price data from the Hermes API const response = await axios.get(`${url}/v2/updates/price/latest?ids[]=${priceId}`); return response.data.binary.data[0]; } catch (error) { console.error('Error:', error.response ? error.response.data : error.message); } } module.exports = { getHermesPriceData }; ``` #### 2) Update Pyth Oracle Contract Price Feed After [fetching an off-chain price feed](#1-fetching-off-chain-price-feed), you can now perform a contract call to the Pyth Oracle to update the price feed on-chain. Call `update_price_feeds` on the Pyth Oracle contract with `data` as your arguments. `example args:` ```json { "data": "504e41550100000000a00100000000010070b0ee3a00d1a3c07ee440887eb34a5a35860e6f4b9230fd62f0593fe35c8a3561735a6a37d269c5f166b84ead8918f710dc1be2ee6b51db5b22340ea2c173fc01673d544b00000000001ae101faedac5851e32b9b23b5f9411a8c2bac4aae3ed4dd7b811dd1a72ea4aa7100000000061bc18c014155575600000000000ab0f04600002710f41bc8c224ed983c68dbf5dab7dd34c9129fecfa03005500ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a600000047e2eb4ef0000000000692480ffffffff800000000673d544b00000000673d544b00000048200e66a00000000005e495a60bb9370c458dd50558b34699b5b179f45e56be22f0a1a0feb1db8469adc8c5efeb53988495bac07bf9efed07f5eee43818150c55055882f6872a228e8e9bc78459ed3ea7fe0b86f3048f6bf0aad34befc46063ab7d200beb8bc9fe5839844d2233546f0742bb665f1e610370fcf8ce5be83d0f47e584b685af87cf3ebcb79e714827dcb99dba579e1a03785052ab3c7c7147d3f7bba822b04dbda159670e9a8d29e7ccf68474b2ca85e00224d29bf65b06b09f95e91703313e053b697b48ac1e4d1c57605a71ab77e7ef276bfe8a369c268333b9a37461bf2b7cb7fd4c005500ecf553770d9b10965f8fb64771e93f5690a182edc32be4a3236e0caaa6e0581a0000000e2ba8cd280000000001b40517fffffff800000000673d544b00000000673d544b0000000e3ea44c6800000000016aee120b47b853f55949284cb8ba0b63824ff9b48cd1da8417f45421b79ee3195fc8d107540a0bbb95c2445b66065754f135cb842db09a7e7ab33f79c546a48db872bd7197b04e3d7b52fbb55b3b9f51707c5a55fac3707cb563dbcde4aadeecc3649c237454cecf519dc567c0da03d81808523aa4fa71815eab25ce7da61b48647bac645d403208135002aab5fde2d7ab3c7c7147d3f7bba822b04dbda159670e9a8d29e7ccf68474b2ca85e00224d29bf65b06b09f95e91703313e053b697b48ac1e4d1c57605a71ab77e7ef276bfe8a369c268333b9a37461bf2b7cb7fd4c" } ``` Example of updating the Pyth Oracle contract's price feed using `near-js/client` and node.js: ``` // near-js imports // https://www.npmjs.com/package/@near-js/client const { nearConnect } = require('../utils/connect'); const { getHermesPriceData } = require('../utils/fetch-hermes-price-data'); const { functionCall } = require('@near-js/client'); const sender = 'your-account.testnet'; const receiver = 'pyth-oracle.testnet'; const network = 'testnet'; const PRICE_IDS = [ // Price ids can be found at https://www.pyth.network/developers/price-feed-ids#near-testnet // NOTE: Ensure you are using NEAR specific price ids & remove the '0x' prefix before using them 'f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b', // BTC/USD price id 'ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6', // ETH/USD price id ]; async function updatePythContractPriceFeeds(network) { // Connect to the NEAR network const { rpcProvider, signer } = nearConnect(sender, network); // Get the price data from the Hermes API // See /utils/fetch-hermes-price-data.js for more details const hermesData = await getHermesPriceData( PRICE_IDS[0], network); // Update the Pyth Oracle contract with the price data // Performs a NEAR function call to the Pyth Oracle contract // Deposit for transaction fee (balance will be refunded) const result = await functionCall({ sender, receiver, method: 'update_price_feeds', args: { data: hermesData }, deposit: 10000000000000000000000, deps: { rpcProvider, signer }, }); console.log( `Transaction 👉 https://testnet.nearblocks.io/txns/${result.outcome.transaction.hash}` ); return result; } updatePythContractPriceFeeds(network); ``` ``` const axios = require('axios'); // There are separate endpoints for testnet and mainnet const HERMES_TESTNET_URL = 'https://hermes-beta.pyth.network'; const HERMES_MAINNET_URL = 'https://hermes.pyth.network'; async function getHermesPriceData(priceId, network) { try { let url; network === 'testnet' ? (url = HERMES_TESTNET_URL) : (url = HERMES_MAINNET_URL); // Fetch the price data from the Hermes API const response = await axios.get(`${url}/v2/updates/price/latest?ids[]=${priceId}`); return response.data.binary.data[0]; } catch (error) { console.error('Error:', error.response ? error.response.data : error.message); } } module.exports = { getHermesPriceData }; ``` ``` // node.js imports const { join } = require('node:path'); const { homedir } = require('node:os'); // near.js imports const { getTestnetRpcProvider, getSignerFromKeystore } = require('@near-js/client'); const { UnencryptedFileSystemKeyStore } = require('@near-js/keystores-node'); // initialize RPC provider and signer const nearConnect = (sender, network) => ({ rpcProvider: getTestnetRpcProvider(), signer: getSignerFromKeystore( sender, network, new UnencryptedFileSystemKeyStore(join(homedir(), '.near-credentials')) ) }); module.exports = { nearConnect }; ``` :::info Although unused deposit will be refunded, you can calculate an estimate by calling the `get_update_fee_estimate` method against the Pyth contract. ::: --- ### `get_price` > Fetches the most recent price feed stored in the Pyth Oracle contract. Is a view method, so does not require a signature or payment. - args: `price_identifier` _(unique price feed identifier)_ - type: `object` - example: `{ price_identifier: 'f9c0172ba10dfa8...' }` After [updating the price feed](#update_price_feeds), you can view the feed on-chain by calling `get_price` on the Pyth Oracle contract. Note that this is a view method and does not require a signature or deposit. `Example:` ``` // near-js import // https://www.npmjs.com/package/@near-js/client const { getTestnetRpcProvider, view } = require('@near-js/client'); const PRICE_IDS = [ // Price ids can be found at https://www.pyth.network/developers/price-feed-ids#near-testnet // NOTE: Ensure you are using NEAR specific price ids & remove the '0x' prefix before using them 'f9c0172ba10dfa4d19088d94f5bf61d3b54d5bd7483a322a982e1373ee8ea31b', // BTC/USD price id 'ca80ba6dc32e08d06f1aa886011eed1d77c77be9eb761cc10d72b7d0a2fd57a6', // ETH/USD price id ]; async function getPrice(price_ID, symbol) { try { const rpcProvider = getTestnetRpcProvider(); const result = await view({ account: 'pyth-oracle.testnet', method: 'get_price', args: { price_identifier: price_ID }, deps: { rpcProvider }, }); console.log(symbol, result); } catch (error) { console.error(`Error fetching ${symbol} price:`, error.message); } } getPrice(PRICE_IDS[0], 'BTC/USD:'); ``` --- # Source: https://docs.near.org/chain-abstraction/omnibridge/overview.md # Source: https://docs.near.org/chain-abstraction/intents/overview.md # Source: https://docs.near.org/ai/shade-agents/tutorials/ai-dao/overview.md --- id: overview title: Overview sidebar_label: Overview description: "A brief overview of the Verifiable AI DAO tutorial built using the Shade Agent Framework that walks through NEAR native deployments, using yield and resume with Shade Agents and leveraging verifiable AI." --- In this tutorial, you'll explore how to build a `fully verifiable AI DAO` using the Shade Agent Framework. The Verifiable AI DAO is a DAO smart contract that uses a Shade Agent with a verifiable LLM to vote on governance proposals according to its predefined manifesto, to create transparent, AI-driven governance that is decentralized and auditable from end-to-end. This tutorial also serves as a `template` for building `yield and resume-based Shade Agents`. This is a smart contract that, when called, halts its execution for the verified agent to complete some logic and resume the transaction when it has a result, enabled by NEAR's asynchronous design. This pattern allows the agent and LLM become a part of the contract, enabling smart contracts with extended capabilities of a backend server that can make API calls and use LLM inference. This is especially useful when making cross-contract calls to smart contracts that use yield and resume, allowing you to receive the result in the callback - for example, an on-demand oracle. --- ## What You'll Learn This tutorial demonstrates how key components of the Shade Agent Framework work together with NEAR's broader tech stack to create a product that is `decentralized and verifiable throughout the entire stack`. The tutorial covers: - **NEAR-native Shade Agent**: How to develop a Shade Agent that operates exclusively on the NEAR blockchain - **Yield and Resume Pattern**: Building a Shade Agent that uses the yield and resume pattern - **Verifiable AI Integration**: Implementing a Shade Agent that uses NEAR AI's verifiable/private inference --- ## Required knowledge To understand this tutorial, you should have familiarity with these concepts: - [Shade Agents](../../getting-started/introduction.md) - [NEAR Smart Contracts](../../../../smart-contracts/what-is.md) - [Custom Agent Contracts](../../reference/custom-agent-contract.md) --- ## Getting Started The complete source code for this tutorial is available in the [verifiable-ai-dao repository](https://github.com/NearDeFi/verifiable-ai-dao). Continue to the [Agent Contract section](./dao-agent-contract.md) to get started and learn about implementing yield and resume-based agent contracts. --- # Source: https://docs.near.org/ai/shade-agents/reference/plugins.md --- id: plugins title: Shade Agent Plugins sidebar_label: Plugins description: "Learn how to use plugins to extend Shade Agent functionality." --- Docs for integrating plugins into your Shade Agent will be coming soon. In the meantime, please feel free to check out these links: - Docs on using [private and verifiable inference](https://docs.near.ai/cloud/get-started/) - An example using a [Twitter API](https://github.com/NearDeFi/shade-agent-basednames) --- # Source: https://docs.near.org/api/rpc/protocol.md --- id: protocol title: Protocol description: Learn how to retrieve the genesis and current protocol configurations hide_table_of_contents: true --- The RPC API enables you to retrieve the current genesis and protocol configuration. --- ## Quick Reference | Method | Parameters | Description | | --- | --- | --- | | [`genesis_config`](#genesis-config) | _none_ | Returns current genesis configuration | | [`EXPERIMENTAL_protocol_config`](#protocol-config) | `finality` OR `block_id` | Returns protocol configuration for latest or specific block | --- ## Genesis Config {#genesis-config} Returns current genesis configuration. - **method**: `genesis_config` - **params**: _none_ ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "genesis_config", } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.experimental_protocolConfig({ sync_checkpoint: 'genesis', }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=genesis_config \ ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "avg_hidden_validator_seats_per_shard": [0], "block_producer_kickout_threshold": 80, "chain_id": "testnet", "chunk_producer_assignment_changes_limit": 5, "chunk_producer_kickout_threshold": 90, "chunk_validator_only_kickout_threshold": 80, "dynamic_resharding": false, "epoch_length": 43200, "fishermen_threshold": "340282366920938463463374607431768211455", "gas_limit": 1000000000000000, "gas_price_adjustment_rate": [1, 100], "genesis_height": 42376888, "genesis_time": "2020-07-31T03:39:42.911378Z", "max_gas_price": "10000000000000000000000", "max_inflation_rate": [1, 20], "max_kickout_stake_perc": 100, "min_gas_price": "5000", "minimum_stake_divisor": 10, "minimum_stake_ratio": [1, 6250], "minimum_validators_per_shard": 1, "num_block_producer_seats": 200, "num_block_producer_seats_per_shard": [200], "num_blocks_per_year": 31536000, "num_chunk_only_producer_seats": 300, "num_chunk_producer_seats": 100, "num_chunk_validator_seats": 300, "online_max_threshold": [99, 100], "online_min_threshold": [90, 100], "protocol_reward_rate": [1, 10], "protocol_treasury_account": "near", "protocol_upgrade_stake_threshold": [4, 5], "protocol_version": 29, "shard_layout": { "V0": { "num_shards": 1, "version": 0 } }, "shuffle_shard_assignment_for_chunk_producers": false, "target_validator_mandates_per_shard": 68, "total_supply": "2089646653180081825096998107194444", "transaction_validity_period": 86400, "use_production_config": false, "validators": [ { "account_id": "masternode24.pool.f863973.m0", "amount": "2096547887468158804726149840014", "public_key": "ed25519:9E3JvrQN6VGDGg1WJ3TjBsNyfmrU6kncBcDvvJLj6qHr" }, { "account_id": "lunanova.pool.f863973.m0", "amount": "6023592217250515747116857534108", "public_key": "ed25519:2fZ59qfo9QHNLijoht9cwUb9enSNcnRmXbQn1gKZxvkw" }, { "account_id": "node0", "amount": "7017386808510582905904716139001", "public_key": "ed25519:7PGseFbWxvYVgZ89K1uTJKYoKetWs7BJtbyXDzfbAcqX" }, { "account_id": "node1", "amount": "7021733510638228632380895173752", "public_key": "ed25519:6DSjZ8mvsRZDvFqFxo8tCKePG96omXW7eVYVSySmDk8e" }, { "account_id": "nodeasy.pool.f863973.m0", "amount": "350028003459257633077889642325", "public_key": "ed25519:25Dhg8NBvQhsVTuugav3t1To1X1zKiomDmnh8yN9hHMb" }, { "account_id": "valeraverim.pool.f863973.m0", "amount": "2460437541222457077732687804254", "public_key": "ed25519:3686ABqNUZc1qhLWLHg5xZpBzrWPiUCMNZxcCNmg3e2s" }, { "account_id": "node2", "amount": "7022280566885326956797181813724", "public_key": "ed25519:GkDv7nSMS3xcqA45cpMvFmfV1o4fRF6zYo1JRR6mNqg5" }, { "account_id": "orangeclub.pool.f863973.m0", "amount": "3073208665436498671483798256985", "public_key": "ed25519:HezFeSzcwuR5wvkqccgMCMnpf1eQkVCfk52tXZEdKZHz" }, { "account_id": "tribe-pool.pool.f863973.m0", "amount": "502021509894008520748060961431", "public_key": "ed25519:CRS4HTSAeiP8FKD3c3ZrCL5pC92Mu1LQaWj22keThwFY" }, { "account_id": "staked.pool.f863973.m0", "amount": "1835541810883701332840668361355", "public_key": "ed25519:D2afKYVaKQ1LGiWbMAZRfkKLgqimTR74wvtESvjx5Ft2" }, { "account_id": "node3", "amount": "7025309465335462891886410729905", "public_key": "ed25519:ydgzeXHJ5Xyt7M1gXLxqLBW1Ejx6scNV5Nx2pxFM8su" }, { "account_id": "moonlet.pool.f863973.m0", "amount": "396044187712024170314465720781", "public_key": "ed25519:3e1nVCVGNS3yr6CcUvpDAs3BhiWtyM9uTBWkyVR5Xn3K" }, { "account_id": "sweden.pool.f863973.m0", "amount": "385869819054217573549654420144", "public_key": "ed25519:2RVUnsMEZhGCj1A3vLZBGjj3i9SQ2L46Z1Z41aEgBzXg" }, { "account_id": "shawnpool.pool.f863973.m0", "amount": "326196336737920044305254508558", "public_key": "ed25519:6dfAfW3oy1kp4u9ePuticHy3Y2WDcHwx8yKSdyLNMPSr" }, { "account_id": "chorus-one.pool.f863973.m0", "amount": "1318859742119402879751178031888", "public_key": "ed25519:6LFwyEEsqhuDxorWfsKcPPs324zLWTaoqk4o6RDXN7Qc" }, { "account_id": "inotel.pool.f863973.m0", "amount": "4945759122706953812641339874642", "public_key": "ed25519:C55jH1MCHYGa3tzUyZZdGrJmmCLP22Aa4v88KYpn2xwZ" }, { "account_id": "p2p.pool.f863973.m0", "amount": "991547852404615467434919132596", "public_key": "ed25519:4ie5979JdSR4f7MRAG58eghRxndVoKnAYAKa1PLoMYSS" }, { "account_id": "dokia.pool.f863973.m0", "amount": "4004628852742744225484204285260", "public_key": "ed25519:935JMz1vLcJxFApG3TY4MA4RHhvResvoGwCrQoJxHPn9" }, { "account_id": "01node.pool.f863973.m0", "amount": "1416856356232757387343764992394", "public_key": "ed25519:3iNqnvBgxJPXCxu6hNdvJso1PEAc1miAD35KQMBCA3aL" }, { "account_id": "legends.pool.f863973.m0", "amount": "303006135607766172564337480878", "public_key": "ed25519:AhQ6sUifJYgjqarXSAzdDZU9ZixpUesP9JEH1Vr7NbaF" }, { "account_id": "blazenet.pool.f863973.m0", "amount": "1892937440977093265954787297596", "public_key": "ed25519:DiogP36wBXKFpFeqirrxN8G2Mq9vnakgBvgnHdL9CcN3" } ] }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Protocol Config {#protocol-config} Returns most recent protocol configuration or a specific queried block. Useful for finding current storage and transaction costs. - **method**: `EXPERIMENTAL_protocol_config` - **params**: - [`finality`](/api/rpc/setup#using-finality-param) _OR_ [`block_id`](/api/rpc/setup#using-block_id-param) ```json { "jsonrpc": "2.0", "id": "dontcare", "method": "EXPERIMENTAL_protocol_config", "params": { "finality": "final" } } ``` ```js import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com", }); const response = await provider.experimental_protocolConfig({ finality: "final" }); ``` ```bash http POST https://rpc.testnet.near.org \ jsonrpc=2.0 \ id=dontcare \ method=EXPERIMENTAL_protocol_config \ params:='{"finality": "final"}' ``` }>
Example response: ```json { "jsonrpc": "2.0", "result": { "avg_hidden_validator_seats_per_shard": [0, 0, 0, 0, 0, 0], "block_producer_kickout_threshold": 80, "chain_id": "testnet", "chunk_producer_kickout_threshold": 80, "chunk_validator_only_kickout_threshold": 70, "dynamic_resharding": false, "epoch_length": 43200, "fishermen_threshold": "340282366920938463463374607431768211455", "gas_limit": 1000000000000000, "gas_price_adjustment_rate": [1, 100], "genesis_height": 42376888, "genesis_time": "2020-07-31T03:39:42.911378Z", "max_gas_price": "10000000000000000000000", "max_inflation_rate": [1, 20], "max_kickout_stake_perc": 30, "min_gas_price": "5000", "minimum_stake_divisor": 10, "minimum_stake_ratio": [1, 62500], "minimum_validators_per_shard": 1, "num_block_producer_seats": 20, "num_block_producer_seats_per_shard": [20, 20, 20, 20, 20, 20], "num_blocks_per_year": 31536000, "num_chunk_only_producer_seats": 0, "online_max_threshold": [99, 100], "online_min_threshold": [90, 100], "protocol_reward_rate": [1, 10], "protocol_treasury_account": "near", "protocol_upgrade_stake_threshold": [4, 5], "protocol_version": 73, "runtime_config": { "account_creation_config": { "min_allowed_top_level_account_length": 65, "registrar_account_id": "registrar" }, "congestion_control_config": { "allowed_shard_outgoing_gas": 1000000000000000, "max_congestion_incoming_gas": 400000000000000000, "max_congestion_memory_consumption": 1000000000, "max_congestion_missed_chunks": 5, "max_congestion_outgoing_gas": 10000000000000000, "max_outgoing_gas": 300000000000000000, "max_tx_gas": 500000000000000, "min_outgoing_gas": 1000000000000000, "min_tx_gas": 20000000000000, "outgoing_receipts_big_size_limit": 4718592, "outgoing_receipts_usual_size_limit": 102400, "reject_tx_congestion_threshold": 0.8 }, "storage_amount_per_byte": "10000000000000000000", "transaction_costs": { "action_creation_config": { "add_key_cost": { "full_access_cost": { "execution": 101765125000, "send_not_sir": 101765125000, "send_sir": 101765125000 }, "function_call_cost": { "execution": 102217625000, "send_not_sir": 102217625000, "send_sir": 102217625000 }, "function_call_cost_per_byte": { "execution": 1925331, "send_not_sir": 47683715, "send_sir": 1925331 } }, "create_account_cost": { "execution": 3850000000000, "send_not_sir": 3850000000000, "send_sir": 3850000000000 }, "delegate_cost": { "execution": 200000000000, "send_not_sir": 200000000000, "send_sir": 200000000000 }, "delete_account_cost": { "execution": 147489000000, "send_not_sir": 147489000000, "send_sir": 147489000000 }, "delete_key_cost": { "execution": 94946625000, "send_not_sir": 94946625000, "send_sir": 94946625000 }, "deploy_contract_cost": { "execution": 184765750000, "send_not_sir": 184765750000, "send_sir": 184765750000 }, "deploy_contract_cost_per_byte": { "execution": 64572944, "send_not_sir": 47683715, "send_sir": 6812999 }, "function_call_cost": { "execution": 780000000000, "send_not_sir": 200000000000, "send_sir": 200000000000 }, "function_call_cost_per_byte": { "execution": 2235934, "send_not_sir": 47683715, "send_sir": 2235934 }, "stake_cost": { "execution": 102217625000, "send_not_sir": 141715687500, "send_sir": 141715687500 }, "transfer_cost": { "execution": 115123062500, "send_not_sir": 115123062500, "send_sir": 115123062500 } }, "action_receipt_creation_config": { "execution": 108059500000, "send_not_sir": 108059500000, "send_sir": 108059500000 }, "burnt_gas_reward": [3, 10], "data_receipt_creation_config": { "base_cost": { "execution": 36486732312, "send_not_sir": 36486732312, "send_sir": 36486732312 }, "cost_per_byte": { "execution": 17212011, "send_not_sir": 47683715, "send_sir": 17212011 } }, "pessimistic_gas_price_inflation_ratio": [103, 100], "storage_usage_config": { "num_bytes_account": 100, "num_extra_bytes_record": 40 } }, "wasm_config": { "alt_bn128": true, "disable_9393_fix": false, "discard_custom_sections": true, "ed25519_verify": true, "eth_implicit_accounts": true, "ext_costs": { "alt_bn128_g1_multiexp_base": 713000000000, "alt_bn128_g1_multiexp_element": 320000000000, "alt_bn128_g1_sum_base": 3000000000, "alt_bn128_g1_sum_element": 5000000000, "alt_bn128_pairing_check_base": 9686000000000, "alt_bn128_pairing_check_element": 5102000000000, "base": 264768111, "bls12381_g1_multiexp_base": 16500000000, "bls12381_g1_multiexp_element": 930000000000, "bls12381_g2_multiexp_base": 18600000000, "bls12381_g2_multiexp_element": 1995000000000, "bls12381_map_fp2_to_g2_base": 1500000000, "bls12381_map_fp2_to_g2_element": 900000000000, "bls12381_map_fp_to_g1_base": 1500000000, "bls12381_map_fp_to_g1_element": 252000000000, "bls12381_p1_decompress_base": 15000000000, "bls12381_p1_decompress_element": 81000000000, "bls12381_p1_sum_base": 16500000000, "bls12381_p1_sum_element": 6000000000, "bls12381_p2_decompress_base": 15000000000, "bls12381_p2_decompress_element": 165000000000, "bls12381_p2_sum_base": 18600000000, "bls12381_p2_sum_element": 15000000000, "bls12381_pairing_base": 2130000000000, "bls12381_pairing_element": 2130000000000, "contract_compile_base": 0, "contract_compile_bytes": 0, "contract_loading_base": 35445963, "contract_loading_bytes": 1089295, "ecrecover_base": 278821988457, "ed25519_verify_base": 210000000000, "ed25519_verify_byte": 9000000, "keccak256_base": 5879491275, "keccak256_byte": 21471105, "keccak512_base": 5811388236, "keccak512_byte": 36649701, "log_base": 3543313050, "log_byte": 13198791, "promise_and_base": 1465013400, "promise_and_per_promise": 5452176, "promise_return": 560152386, "read_cached_trie_node": 2280000000, "read_memory_base": 2609863200, "read_memory_byte": 3801333, "read_register_base": 2517165186, "read_register_byte": 98562, "ripemd160_base": 853675086, "ripemd160_block": 680107584, "sha256_base": 4540970250, "sha256_byte": 24117351, "storage_has_key_base": 54039896625, "storage_has_key_byte": 30790845, "storage_iter_create_from_byte": 0, "storage_iter_create_prefix_base": 0, "storage_iter_create_prefix_byte": 0, "storage_iter_create_range_base": 0, "storage_iter_create_to_byte": 0, "storage_iter_next_base": 0, "storage_iter_next_key_byte": 0, "storage_iter_next_value_byte": 0, "storage_large_read_overhead_base": 1, "storage_large_read_overhead_byte": 1, "storage_read_base": 56356845749, "storage_read_key_byte": 30952533, "storage_read_value_byte": 5611004, "storage_remove_base": 53473030500, "storage_remove_key_byte": 38220384, "storage_remove_ret_value_byte": 11531556, "storage_write_base": 64196736000, "storage_write_evicted_byte": 32117307, "storage_write_key_byte": 70482867, "storage_write_value_byte": 31018539, "touching_trie_node": 16101955926, "utf16_decoding_base": 3543313050, "utf16_decoding_byte": 163577493, "utf8_decoding_base": 3111779061, "utf8_decoding_byte": 291580479, "validator_stake_base": 911834726400, "validator_total_stake_base": 911834726400, "write_memory_base": 2803794861, "write_memory_byte": 2723772, "write_register_base": 2865522486, "write_register_byte": 3801564, "yield_create_base": 153411779276, "yield_create_byte": 15643988, "yield_resume_base": 1195627285210, "yield_resume_byte": 47683715 }, "fix_contract_loading_cost": false, "function_call_weight": true, "grow_mem_cost": 1, "implicit_account_creation": true, "limit_config": { "account_id_validity_rules_version": 1, "contract_prepare_version": 2, "initial_memory_pages": 1024, "max_actions_per_receipt": 100, "max_arguments_length": 4194304, "max_contract_size": 4194304, "max_functions_number_per_contract": 10000, "max_gas_burnt": 300000000000000, "max_length_method_name": 256, "max_length_returned_data": 4194304, "max_length_storage_key": 2048, "max_length_storage_value": 4194304, "max_locals_per_contract": 1000000, "max_memory_pages": 2048, "max_number_bytes_method_names": 2000, "max_number_input_data_dependencies": 128, "max_number_logs": 100, "max_number_registers": 100, "max_promises_per_function_call_action": 1024, "max_receipt_size": 4194304, "max_register_size": 104857600, "max_stack_height": 262144, "max_total_log_length": 16384, "max_total_prepaid_gas": 300000000000000, "max_transaction_size": 1572864, "max_yield_payload_size": 1024, "per_receipt_storage_proof_size_limit": 4000000, "registers_memory_limit": 1073741824, "wasmer2_stack_limit": 204800, "yield_timeout_length_in_blocks": 200 }, "math_extension": true, "regular_op_cost": 822756, "storage_get_mode": "FlatStorage", "vm_kind": "NearVm", "yield_resume_host_functions": true }, "witness_config": { "combined_transactions_size_limit": 4194304, "main_storage_proof_size_soft_limit": 4000000, "new_transactions_validation_state_size_soft_limit": 572864 } }, "shard_layout": { "V1": { "boundary_accounts": [ "aurora", "aurora-0", "game.hot.tg", "kkuuue2akv_1630967379.near", "tge-lockup.sweat" ], "shards_split_map": [[0], [1], [2, 3], [4], [5]], "to_parent_shard_map": [0, 1, 2, 2, 3, 4], "version": 3 } }, "shuffle_shard_assignment_for_chunk_producers": false, "target_validator_mandates_per_shard": 68, "transaction_validity_period": 86400 }, "id": "dontcare" } ```
Error handling: When making RPC API requests, you may encounter various errors related to network configuration, rate limiting, or request formatting. For comprehensive information about error types, causes, and solutions, see the [RPC Errors](/api/rpc/errors) documentation.
--- ## Best Practices - Use `finality: "final"` for most recent confirmed protocol configuration - Use specific `block_id` when you need protocol config for a particular block - Cache protocol configuration results as they change infrequently - Use the protocol config to calculate current storage and transaction costs - Handle network timeouts gracefully in production applications --- --- # Source: https://docs.near.org/api/rpc/providers.md --- id: providers title: RPC Providers description: "Discover NEAR RPC providers for mainnet and testnet" --- There are multiple RPC providers from which you can choose from. These providers will work as intermediaries to help you interact with the NEAR network. You'll experience different latency levels depending on the provider's location. You can potentially use multiple providers for redundancy and balancing. :::tip You can use any of these RPC endpoints with [any of our APIs](../../tools/near-api.md#view-function) or [wallet libraries](../../web3-apps/tutorials/wallet-login.md) ::: ## Mainnet | Provider | Endpoint Root | Public Endpoint | Archival Node | Free Tier | | -------------------------------------------------------------------------- | ------------------------------------------------------------ | ----------------- | ----------------- | --------- | | [1RPC](https://docs.1rpc.io/) | `https://1rpc.io/near` | :heavy_check_mark: | | :heavy_check_mark: (20k req/day) | | [All That Node](https://www.allthatnode.com/) | `https://allthatnode.com/protocol/near.dsrv` | | :heavy_check_mark: | :heavy_check_mark: (500,000 CU/day, 150 cups) | | [ankr.com](https://www.ankr.com/docs/rpc-service/chains/chains-list/#near) | `https://rpc.ankr.com/near` | | | ❌ | | [BlockPI Network](https://blockpi.io) | `https://near.blockpi.network/v1/rpc/public` | :heavy_check_mark: | | :heavy_check_mark: (50M RUs/month, 20 req/sec) | | [dRPC](https://drpc.org/) | `https://near.drpc.org` | :heavy_check_mark: | |:heavy_check_mark: (210M CU /month, 100 req/sec) | | [fast-near web4](https://github.com/vgrichina/fast-near) | `https://rpc.web4.near.page` | :heavy_check_mark: | | :heavy_check_mark: | | [FASTNEAR](https://fastnear.com) | `https://free.rpc.fastnear.com` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | [GetBlock](https://getblock.io/nodes/near/) | `https://getblock.io/nodes/near/` | :heavy_check_mark: | | :heavy_check_mark: (50K CU/day, 20 RPS, 2 access tokens ) | | [Grove](https://grove.city) | `https://near.rpc.grove.city/v1/01fdb492` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | [Lava Network](https://www.lavanet.xyz/get-started/near) | `https://near.lava.build:443` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | | [Intear RPC](https://rainy.intea.rs) | `https://rpc.intea.rs` | :heavy_check_mark: | | :heavy_check_mark: | | [Lavender.Five Nodes](https://lavenderfive.com/) | `https://near.lavenderfive.com/` | | | ❌ | | [NodeReal](https://nodereal.io) | `https://nodereal.io/api-marketplace/near-rpc` | | | :heavy_check_mark: (100,000 CUs/month , 300 CUPS ) | | [NOWNodes](https://nownodes.io/) | `https://near.nownodes.io/` | | | :heavy_check_mark: (100 000 req/month, 1 API key, 15 RPS limit )| | [OMNIA](https://omniatech.io) | `https://endpoints.omniatech.io/v1/near/mainnet/public` | :heavy_check_mark: | | ❌ | | [QuickNode](https://www.quicknode.com/chains/near) | - | | | :heavy_check_mark: (15 RPS, 10M API credits/month) | | [Seracle](https://docs.seracle.com/) | `https://api.seracle.com/saas/baas/rpc/near/mainnet/public/` | | | ❌ | | [Shitzu](https://shitzuapes.xyz/) | `https://rpc.shitzuapes.xyz` | :heavy_check_mark: | | :heavy_check_mark: | | [Tatum](https://tatum.io/chain/near/) | `https://near-mainnet.gateway.tatum.io/` | | |:heavy_check_mark:( 3 Req / sec ) | | [ZAN](https://zan.top/service/apikeys) | `https://api.zan.top/node/v1/near/mainnet/` | :heavy_check_mark: | | :heavy_check_mark: | | [Zeeve](https://www.zeeve.io/) | - | :heavy_check_mark: | :heavy_check_mark: | ❌ | FastNear maintains [a comprehensive Grafana dashboard](https://grafana.fastnear.com/public-dashboards/577b37c6cfe84b2bae23af471d27cade) with response times for most of the publicly and freemium available endpoints. ## Testnet | Provider | Endpoint Root | Public Endpoint | | -------------------------------------------------------------------------- | ------------------------------------------------------------ | ------------------ | | [FASTNEAR](https://fastnear.com) | `https://test.rpc.fastnear.com` | :heavy_check_mark: | | [ZAN](https://zan.top/service/apikeys) | `https://api.zan.top/node/ws/v1/near/testnet` | | | [All That Node](https://www.allthatnode.com/) | `https://allthatnode.com/protocol/near.dsrv` | | | [dRPC](https://drpc.org/) | `https://near-testnet.drpc.org` | :heavy_check_mark: | | [fast-near web4](https://github.com/vgrichina/fast-near) | `https://rpc.web4.testnet.page/account/testnet` | :heavy_check_mark: | | [FASTNEAR](https://fastnear.com) | `https://rpc.testnet.fastnear.com` | :heavy_check_mark: | | [Intear RPC](https://rainy.intea.rs) | `https://testnet-rpc.intea.rs` | :heavy_check_mark: | | [Lava Network](https://www.lavanet.xyz/get-started/near) | `https://neart.lava.build:443` | :heavy_check_mark: | | [QuickNode](https://www.quicknode.com/chains/near) | - | | | [Tatum](https://tatum.io/chain/near/) | `https://near-testnet.gateway.tatum.io/` | | | [Zeeve](https://www.zeeve.io/) | - | | ## RPC Failover In `near-api-js` you can use [`FailoverRpcProvider`](https://github.com/near-examples/near-api-examples/blob/main/near-api-js/examples/rpc-failover.ts) to automatically switch RPC providers when one provider is experiencing downtime, or implement an RPC selection widget that allows users to add their own RPC provider. As a user, if a dApp or wallet doesn't support RPC failover and the primary provider is down, you can use an RPC Selector browser extension to redirect all requests to an RPC provider of your choice. ## On NEAR.org RPC Deprecation Please read the following announcement: [Deprecation of NEAR.org and Pagoda.co RPC Endpoints](https://near.org/blog/deprecation-of-near-org-and-pagoda-co-rpc-endpoints/). > After careful consideration and approval by the Infrastructure Committee, NEAR will implement a phased deprecation of the free public RPC endpoints under `near.org` and `pagoda.co`, beginning June 1, 2025. This deprecation aims to create a more sustainable and decentralized ecosystem, and follows [Pagoda winding down operations](https://near.org/blog/ecosystem-update-announcing-near-one-chain-abstraction-spinouts) and decentralizing its functions into NEAR ecosystem teams and committees. All public RPC endpoints under the `near.org` and `pagoda.co` domains, including both regular and archival endpoints, will undergo a gradual deprecation through increasingly restricted rate limits. These changes will affect all of the following endpoints: - `rpc.*.near.org` - `archival-rpc.*.near.org` - `rpc.*.pagoda.co` - `archival-rpc.*.pagoda.co` - Additional related endpoints (v2, internal, beta) ### Rate-limits of NEAR.org RPC endpoints New 10 minutes rate-limits will be implemented to prevent production usage of the RPC endpoints. The deprecation will occur in three phases: ### Phase 1: Beginning June 1st, 2025 - RPC Mainnet: 120 RPM, 600 requests per 10 minutes - RPC Testnet: 120 RPM, 600 requests per 10 minutes - Archival-RPC Mainnet: 16 RPM, 80 requests per 10 minutes - Archival-RPC Testnet: 80 RPM, 400 requests per 10 minutes ### Phase 2: Beginning July 1st, 2025 - RPC Mainnet: 60 RPM, 150 requests per 10 minutes - RPC Testnet: 60 RPM, 150 requests per 10 minutes - Archival-RPC Mainnet: 8 RPM, 20 requests per 10 minutes - Archival-RPC Testnet: 40 RPM, 100 requests per 10 minutes ### Phase 3: Beginning August 1st, 2025 - RPC Mainnet: 30 RPM, 75 requests per 10 minutes - RPC Testnet: 30 RPM, 75 requests per 10 minutes - Archival-RPC Mainnet: 4 RPM, 10 requests per 10 minutes - Archival-RPC Testnet: 20 RPM, 50 requests per 10 minutes After August 1st, FastNear will continue to maintain these endpoints with the final reduced rate limits to prevent complete breakage of legacy tools, but they will be considered fully deprecated. **We strongly recommend that all developers migrate away from these endpoints entirely by June 1st, 2025.** After this date, any large-scale backend usage may be blocked by IP address. The short-term burst limits combined with tight per-minute restrictions will make these endpoints unsuitable for continuous usage. --- # Source: https://docs.near.org/data-infrastructure/tutorials/python-nft-indexer.md --- sidebar_label: NFT indexer for Python description: "Learn to build a Python NFT indexer with NEAR Lake Framework" --- This tutorial shows how to build a fully functional NFT indexer built using the [NEAR Lake Framework for Python](/data-infrastructure/near-lake-framework), which monitors `nft_mint` [events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) and outputs relevant details such as the `receipt_id` of the `Receipt` where the mint occurred, the marketplace, the NFT owner's account name, and links to the NFTs on the marketplaces. The final source code is available on the GitHub [`frolvanya/near-lake-nft-indexer`](https://github.com/frolvanya/near-lake-nft-indexer) :::note Source code for the tutorial [`frolvanya/near-lake-nft-indexer`](https://github.com/frolvanya/near-lake-nft-indexer): source code for this tutorial ::: ## Motivation NEAR Protocol had introduced a nice feature [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md). The Events allow a contract developer to add standardized logs to the `ExecutionOutcomes` thus allowing themselves or other developers to read those logs in more convenient manner via API or indexers. The Events have a field `standard` which aligns with NEPs. In this tutorial we'll be talking about [NEP-171 Non-Fungible Token standard](https://github.com/near/NEPs/discussions/171). In this tutorial our goal is to show you how you can "listen" to the Events contracts emit and how you can benefit from them. As the example we will be building an indexer that watches all the NFTs minted following the [NEP-171 Events](https://github.com/near/NEPs/tree/master/neps/nep-0256.md) standard, assuming we're collectors who don't want to miss a thing. Our indexer should notice every single NFT minted and give us a basic set of data like: in what Receipt it was minted, and show us the link to a marketplace (we'll cover [Paras](https://paras.id) and [Mintbase/Bitte](https://bitte.ai/) in our example). We will use Python version of [NEAR Lake Framework](/data-infrastructure/near-lake-framework) in this tutorial. Though the concept is the same for Rust, but we want to show more people that it's not that complex to build your own indexer. ## Preparation :::danger Credentials Please, ensure you've the credentials set up as described on the [Credentials](./running-near-lake/credentials.md) page. Otherwise you won't be able to get the code working. ::: Let's create our project folder ```bash mkdir lake-nft-indexer && cd lake-nft-indexer touch main.py ``` ## Set up NEAR Lake Framework In the `main.py` let's import `stream` function and `near_primitives` from `near-lake-framework`: ```python title=main.py from near_lake_framework import near_primitives, LakeConfig, streamer ``` Add the main function ```python title=main.py async def main(): print("Starting NFT indexer") ``` Add the instantiation of `LakeConfig` below: ```python title=main.py config = LakeConfig.mainnet() config.start_block_height = 69030747 config.aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") config.aws_secret_key = os.getenv("AWS_SECRET_ACCESS_KEY") ``` Just a few words on the config, function `mainnet()` has set `s3_bucket_name`, `s3_region_name` for mainnet. You can go to [NEAR Explorer](https://nearblocks.io) and get **the most recent** block height to set `config.start_block_height`. Let's call `streamer` function with the `config` ```python title=main.py stream_handle, streamer_messages_queue = streamer(config) while True: streamer_message = await streamer_messages_queue.get() ``` And an actual start of our indexer in the end of the `main.py` ```python title=main.py loop = asyncio.get_event_loop() loop.run_until_complete(main()) ``` The final `main.py` at this moment should look like the following: ```python title=main.py from near_lake_framework import LakeConfig, streamer, near_primitives async def main(): print("Starting NFT indexer") config = LakeConfig.mainnet() config.start_block_height = 69030747 config.aws_access_key_id = os.getenv("AWS_ACCESS_KEY_ID") config.aws_secret_key = os.getenv("AWS_SECRET_ACCESS_KEY") stream_handle, streamer_messages_queue = streamer(config) while True: streamer_message = await streamer_messages_queue.get() loop = asyncio.get_event_loop() loop.run_until_complete(main()) ``` Now we need to create a callback function that we'll be called to handle `StreamerMessage` our indexer receives. ```python title=main.py async def handle_streamer_message(streamer_message: near_primitives.StreamerMessage): pass ``` ## Events and where to catch them First of all let's find out where we can catch the Events. We hope you are familiar with how the [Data Flow in NEAR Blockchain](/protocol/data-flow/near-data-flow), but let's revise our knowledge: - Mint an NFT is an action in an NFT contract (doesn't matter which one) - Actions are located in a `Receipt` - A result of the Receipt execution is `ExecutionOutcome` - `ExecutionOutcome` in turn, catches the logs a contract "prints" - [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) built on top of the logs This leads us to the realization that we can watch only for ExecutionOutcomes and ignore everything else `StreamerMessage` brings us. ## Catching only the data we need Inside the callback function `handle_streamer_message` we've prepared in the [Preparation](#preparation) section let's start filtering the data we need: ```python title=main.py async def handle_streamer_message(streamer_message: near_primitives.StreamerMessage): for shard in streamer_message.shards: for receipt_execution_outcome in shard.receipt_execution_outcomes: for log in receipt_execution_outcome.execution_outcome.outcome.logs: pass ``` We have iterated through the logs of all ExecutionOutcomes of `Shards` (in our case we don't care on which Shard did the mint happen) Now we want to deal only with those ExecutionOutcomes that contain logs of Events format. Such logs start with `EVENT_JSON:` according to the [Events docs](https://github.com/near/NEPs/tree/master/neps/nep-0297.md#events). ```python title=main.py async def handle_streamer_message(streamer_message: near_primitives.StreamerMessage): for shard in streamer_message.shards: for receipt_execution_outcome in shard.receipt_execution_outcomes: for log in receipt_execution_outcome.execution_outcome.outcome.logs: if not log.startswith("EVENT_JSON:"): continue try: parsed_log = json.loads(log[len("EVENT_JSON:") :]) except json.JSONDecodeError: print( f"Receipt ID: `{receipt_execution_outcome.receipt.receipt_id}`\nError during parsing logs from JSON string to dict" ) continue ``` Let us explain what we are doing here: 1. We are walking through the logs in ExecutionOutcomes 2. We are filtering ExecutionOutcomes that contain logs of Events format 3. In order to collect the Events we are iterating through the ExecutionOutcome's logs trying to parse Event using `json.loads` The goal for our indexer is to return the useful data about a minted NFT that follows NEP-171 standard. We need to drop irrelevant standard Events: ```python title=main.py if ( parsed_log.get("standard") != "nep171" or parsed_log.get("event") != "nft_mint" ): continue ``` ## Almost done So far we have collected everything we need corresponding to our requirements. The final look of the `handle_streamer_message` function: ```python title=main.py async def handle_streamer_message(streamer_message: near_primitives.StreamerMessage): for shard in streamer_message.shards: for receipt_execution_outcome in shard.receipt_execution_outcomes: for log in receipt_execution_outcome.execution_outcome.outcome.logs: if not log.startswith("EVENT_JSON:"): continue try: parsed_log = json.loads(log[len("EVENT_JSON:") :]) except json.JSONDecodeError: print( f"Receipt ID: `{receipt_execution_outcome.receipt.receipt_id}`\nError during parsing logs from JSON string to dict" ) continue if ( parsed_log.get("standard") != "nep171" or parsed_log.get("event") != "nft_mint" ): continue print(parsed_log) ``` Now let's call `handle_streamer_message` inside the loop in `main` function ```python title=main.py await handle_streamer_message(streamer_message) ``` And if we run our indexer we will be catching `nft_mint` event and print logs in the terminal. ```bash python3 main.py ``` :::note Having troubles running the indexer? Please, check you haven't skipped the [Credentials](./running-near-lake/credentials.md) part :) ::: Not so fast! Remember we were talking about having the links to the marketplaces to see the minted tokens? We're gonna extend our data with links whenever possible. At least we're gonna show you how to deal with the NFTs minted on [Paras](https://paras.id) and [Mintbase/Bitte](https://bitte.ai/). ## Crafting links to Paras and Mintbase for NFTs minted there At this moment we have an access to logs that follows NEP-171 standard. We definitely know that all the data we have at this moment are relevant for us, and we want to extend it with the links to that minted NFTs at least for those marketplaces we know. We know and love Paras and Mintbase. ### Paras token URL We did the research for you and here's how the URL to token on Paras is crafting: ``` https://paras.id/token/[1]::[2]/[3] ``` Where: - [1] - Paras contract address (`x.paras.near`) - [2] - First part of the `token_id` (`parsed_log["data"]` for Paras is an array of objects with `token_ids` key in it. Those IDs represented by numbers with column `:` between them) - [3] - `token_id` itself Example: ``` https://paras.id/token/x.paras.near::387427/387427:373 ``` ### Mintbase token URL And again we did the research for you: ``` https://bitte.ai//thing/[1]:[2] ``` Where: - [1] - `meta_id` (`parsed_log["data"]` for Mintbase is an array of stringified JSON that contains `meta_id`) - [2] - Store contract account address (basically Receipt's receiver ID) Example: ``` https://bitte.ai/thing/70eES-icwSw9iPIkUluMHOV055pKTTgQgTiXtwy3Xus:vnartistsdao.mintbase1.near ``` Let's start crafting the links: ```python title=main.py def format_paras_nfts(data, receipt_execution_outcome): links = [] for data_element in data: for token_id in data_element.get("token_ids", []): first_part_of_token_id = token_id.split(":")[0] links.append( f"https://paras.id/token/{receipt_execution_outcome.receipt.receiver_id}::{first_part_of_token_id}/{token_id}" ) return {"owner": data[0].get("owner_id"), "links": links} def format_mintbase_nfts(data, receipt_execution_outcome): links = [] for data_block in data: try: memo = json.loads(data_block.get("memo")) except json.JSONDecodeError: print( f"Receipt ID: `{receipt_execution_outcome.receipt.receipt_id}`\nMemo: `{memo}`\nError during parsing Mintbase memo from JSON string to dict" ) return meta_id = memo.get("meta_id") links.append( f"https://bitte.ai/thing/{meta_id}:{receipt_execution_outcome.receipt.receiver_id}" ) return {"owner": data[0].get("owner_id"), "links": links} ``` We're going to print the receipt_id, so you would be able to search for it on [NEAR Explorer](https://nearblocks.io), marketplace name and the list of links to the NFTs along with the owner account name. ```python title=main.py if receipt_execution_outcome.receipt.receiver_id.endswith( ".paras.near" ): output = { "receipt_id": receipt_execution_outcome.receipt.receipt_id, "marketplace": "Paras", "nfts": format_paras_nfts( parsed_log["data"], receipt_execution_outcome ), } ``` A few words about what is going on here. If the Receipt's receiver account name ends with `.paras.near` (e.g. `x.paras.near`) we assume it's from Paras marketplace, so we are changing the corresponding variable. Mintbase turn, we hope [Nate](https://twitter.com/nategeier) and his team have migrated to [NEAR Lake Framework](./near-lake-framework.md) already, saying "Hi!" and crafting the link: ```python title=main.py elif re.search( ".mintbase\d+.near", receipt_execution_outcome.receipt.receiver_id ): output = { "receipt_id": receipt_execution_outcome.receipt.receipt_id, "marketplace": "Mintbase", "nfts": format_mintbase_nfts( parsed_log["data"], receipt_execution_outcome ), } else: continue ``` Almost the same story as with Paras, but a little bit more complex. The nature of Mintbase marketplace is that it's not a single marketplace! Every Mintbase user has their own store and a separate contract. And it looks like those contract addresses follow the same principle they end with `.mintbaseN.near` where `N` is number (e.g. `nate.mintbase1.near`). After we have defined that the ExecutionOutcome receiver is from Mintbase we are doing the same stuff as with Paras: 1. Setting the `marketplace` variable to Mintbase 2. Collecting the list of NFTs with owner and crafted links And make it print the output to the terminal: ```python title=main.py print(json.dumps(output, indent=4)) ``` All together: ```python title=main.py def format_paras_nfts(data, receipt_execution_outcome): links = [] for data_element in data: for token_id in data_element.get("token_ids", []): first_part_of_token_id = token_id.split(":")[0] links.append( f"https://paras.id/token/{receipt_execution_outcome.receipt.receiver_id}::{first_part_of_token_id}/{token_id}" ) return {"owner": data[0].get("owner_id"), "links": links} def format_mintbase_nfts(data, receipt_execution_outcome): links = [] for data_block in data: try: memo = json.loads(data_block.get("memo")) except json.JSONDecodeError: print( f"Receipt ID: `{receipt_execution_outcome.receipt.receipt_id}`\nMemo: `{memo}`\nError during parsing Mintbase memo from JSON string to dict" ) return meta_id = memo.get("meta_id") links.append( f"https://bitte.ai/thing/{meta_id}:{receipt_execution_outcome.receipt.receiver_id}" ) return {"owner": data[0].get("owner_id"), "links": links} async def handle_streamer_message(streamer_message: near_primitives.StreamerMessage): for shard in streamer_message.shards: for receipt_execution_outcome in shard.receipt_execution_outcomes: for log in receipt_execution_outcome.execution_outcome.outcome.logs: if not log.startswith("EVENT_JSON:"): continue try: parsed_log = json.loads(log[len("EVENT_JSON:") :]) except json.JSONDecodeError: print( f"Receipt ID: `{receipt_execution_outcome.receipt.receipt_id}`\nError during parsing logs from JSON string to dict" ) continue if ( parsed_log.get("standard") != "nep171" or parsed_log.get("event") != "nft_mint" ): continue if receipt_execution_outcome.receipt.receiver_id.endswith( ".paras.near" ): output = { "receipt_id": receipt_execution_outcome.receipt.receipt_id, "marketplace": "Paras", "nfts": format_paras_nfts( parsed_log["data"], receipt_execution_outcome ), } elif re.search( ".mintbase\d+.near", receipt_execution_outcome.receipt.receiver_id ): output = { "receipt_id": receipt_execution_outcome.receipt.receipt_id, "marketplace": "Mintbase", "nfts": format_mintbase_nfts( parsed_log["data"], receipt_execution_outcome ), } else: continue print(json.dumps(output, indent=4)) ``` And not that's it. Run the indexer to watch for NFT minting and never miss a thing. ```bash python3 main.py ``` :::note Having troubles running the indexer? Please, check you haven't skipped the [Credentials](./running-near-lake/credentials.md) part :) ::: Example output: ``` { "receipt_id": "8rMK8rxb3WmFcSfM3ahFoeeoBF92pad3tpsqKoSWurr2", "marketplace": "Mintbase", "nfts": { "owner": "vn-artists-dao.near", "links": [ "https://bitte.ai/thing/aqdCBHB9_2XZY7pwXRRu5rGDeLQl7Q8KgNud1wKgnGo:vnartistsdao.mintbase1.near" ] } } { "receipt_id": "ArRh94Fe1LKF9yPrAdzrMozWoxMVQbEW2Z2Zf4fsSsce", "marketplace": "Paras", "nfts": { "owner": "eeaeb516e0945893ac01eaf547f499abdbd344831c5fcbefa1a5c0a9f303cc5c", "links": [ "https://paras.id/token/x.paras.near::432714/432714:1" ] } } ``` ## Conclusion What a ride, yeah? Let's sum up what we have done: - You've learnt about [Events](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) - Now you understand how to follow for the Events - Knowing the fact that as a contract developer you can use Events and emit your own events, now you know how to create an indexer that follows those Events - We've had a closer look at NFT minting process, you can experiment further and find out how to follow `nft_transfer` Events The material from this tutorial can be extrapolated for literally any event that follows the [Events format](https://github.com/near/NEPs/tree/master/neps/nep-0297.md) Not mentioning you have a dedicated indexer to find out about the newest NFTs minted out there and to be the earliest bird to collect them. Let's go hunt doo, doo, doo 🦈 :::note Source code for the tutorial [`near-examples/near-lake-nft-indexer`](https://github.com/near-examples/near-lake-nft-indexer): source code for this tutorial ::: --- # Source: https://docs.near.org/web3-apps/quickstart.md # Source: https://docs.near.org/smart-contracts/quickstart.md --- id: quickstart title: Your First Smart Contract sidebar_label: Quickstart description: "Create your first contract using your favorite language." --- Welcome! [NEAR accounts](../protocol/account-model.md) can store small apps known as smart contracts. In this quick tutorial, we will guide you in creating your first contract on the NEAR **testnet**! Join us in creating a friendly auction contract, which allows users to place bids, track the highest bidder and claim tokens at the end of the auction.
Prefer an online IDE? Want to jump right into the code without setting up a local dev environment? Check out [NEAR Playground](https://nearplay.app/) for an easy-to-use online IDE with pre-configured templates. ![NEAR Playground](@site/static/assets/docs/smart-contracts/NEAR-Playground.png)
--- ## Prerequisites Before starting, make sure to set up your development environment.
Working on Windows? See our blog post [getting started on NEAR using Windows](/blog/getting-started-on-windows) for a step-by-step guide on how to set up WSL and your environment
```bash # Install Rust: https://www.rust-lang.org/tools/install curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh # Contracts will be compiled to wasm, so we need to add the wasm target rustup target add wasm32-unknown-unknown # Install NEAR CLI-RS to deploy and interact with the contract curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh # Install cargo near to help building the contract curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/cargo-near/releases/latest/download/cargo-near-installer.sh | sh ``` ```bash # Install Node.js using nvm (more options in: https://nodejs.org/en/download) curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash nvm install latest # ⚠️ For Mac Silicon users only, Rosetta is needed to compile contracts # /usr/sbin/softwareupdate --install-rosetta --agree-to-license # Install NEAR CLI to deploy and interact with the contract npm install -g near-cli-rs@latest ``` ```bash # Install Python (if not already installed) # Use your system's package manager or download from https://www.python.org/downloads/ # Install Emscripten (required for compiling Python contracts to WebAssembly) # For Linux/macOS: git clone https://github.com/emscripten-core/emsdk.git cd emsdk ./emsdk install latest ./emsdk activate latest source ./emsdk_env.sh # Add to your .bashrc or .zshrc for permanent installation: # echo 'source "/path/to/emsdk/emsdk_env.sh"' >> ~/.bashrc cd .. # For Windows: # Download and extract: https://github.com/emscripten-core/emsdk # Then in Command Prompt: # cd emsdk # emsdk install latest # emsdk activate latest # emsdk_env.bat # Verify installation with: emcc --version # Install uv for Python package management curl -LsSf https://astral.sh/uv/install.sh | sh # Install NEAR CLI-RS to deploy and interact with the contract curl --proto '=https' --tlsv1.2 -LsSf https://github.com/near/near-cli-rs/releases/latest/download/near-cli-rs-installer.sh | sh ``` ```bash #For Linux arm/x64 sudo apt update && sudo apt upgrade -y sudo apt install -y build-essential curl wget git libssl-dev pkg-config checkinstall sudo apt install bison #For Mac xcode-select --install brew update brew install mercurial brew install binaryen bash < <(curl -s -S -L https://raw.githubusercontent.com/moovweb/gvm/master/binscripts/gvm-installer) gvm install go1.25.4 -B gvm use go1.25.4 --default curl -LO https://github.com/vlmoon99/near-cli-go/releases/latest/download/install.sh && bash install.sh ``` --- ## Creating the Contract Create a smart contract by using one of the scaffolding tools and following their instructions: ```bash cargo near ``` ![img](@site/static/assets/docs/smart-contracts/hello-near-rs.gif) _Creating a project using `cargo near new`_ ```bash npx create-near-app@latest ``` ![img](@site/static/assets/docs/smart-contracts/hello-near-ts.gif) _Creating a project using `npx create-near-app@latest`_ :::important When prompted to choose a template, select the basic `Auction` template to scaffold the auction contract ::: :::note Python quickstart tutorial is coming soon! In the meantime, please check out the [hello-near](https://github.com/near-examples/hello-near-examples/tree/main/contract-py) example. ::: :::tip For this tutorial we chose to name the project `auction`, but feel free to use any name you prefer ::: --- ## What Does This Contract Do? The auction smart contract allows users to place bids, track the highest bidder and claim tokens at the end of the auction. Do not worry about the code just yet — for now, it is enough to know that the most relevant function is `bid`, which allows users to place bids by attaching NEAR tokens: ### Language Comparison | Language | Best For | Build Time | Ecosystem Maturity | |----------|----------|------------|-------------------| | **JavaScript/TypeScript** | Rapid prototyping, web developers | Fast (~2s) | Mature | | **Rust** | Production apps, performance-critical code | Moderate (~30s) | Most mature | | **Python** | Data science, AI integration | Fast (~3s) | Growing | | **Go** | Backend developers, microservices | Fast (~5s) | New | Choose JavaScript for quick experiments, Rust for production, Python for AI/data apps, Go if you're already a Go developer. ``` #[payable] pub fn bid(&mut self) -> Promise { // Assert the auction is still ongoing require!( env::block_timestamp() < self.auction_end_time.into(), "Auction has ended" ); // Current bid let bid = env::attached_deposit(); let bidder = env::predecessor_account_id(); // Last bid let Bid { bidder: last_bidder, bid: last_bid, } = self.highest_bid.clone(); // Check if the deposit is higher than the current bid require!(bid > last_bid, "You must place a higher bid"); // Update the highest bid self.highest_bid = Bid { bidder, bid }; // Transfer tokens back to the last bidder Promise::new(last_bidder).transfer(last_bid) } ``` ``` @call({ payableFunction: true }) bid(): NearPromise { // Assert the auction is still ongoing assert(this.auction_end_time > near.blockTimestamp(), "Auction has ended"); // Current bid const bid = near.attachedDeposit(); const bidder = near.predecessorAccountId(); // Last bid const { bidder: lastBidder, bid: lastBid } = this.highest_bid; // Check if the deposit is higher than the current bid assert(bid > lastBid, "You must place a higher bid"); // Update the highest bid this.highest_bid = { bidder, bid }; // Save the new bid // Transfer tokens back to the last bidder return NearPromise.new(lastBidder).transfer(lastBid); } ``` ``` @call def bid(self): """ Place a bid in the auction. Must be higher than the current highest bid. """ # Assert the auction is still ongoing assert self.block_timestamp < self.storage["auction_end_time"], "Auction has ended" # Current bid bid = self.attached_deposit bidder = self.predecessor_account_id # Last bid last_bidder = self.storage["highest_bidder"] last_bid = self.storage["highest_bid"] # Check if the deposit is higher than the current bid assert bid > last_bid, "You must place a higher bid" # Update the highest bid self.storage["highest_bidder"] = bidder self.storage["highest_bid"] = bid # Transfer tokens back to the last bidder return Promise.create_batch(last_bidder).transfer(last_bid) @call ``` Besides `bid`, the contract exposes methods to initialize the auction (`init`), query the highest bidder (`get_highest_bid`), and claim tokens once the auction ends (`claim`). --- ## Test the Contract Lets make sure the contract is working as expected by running its tests. Simply run the `test` command, the contract will then be compiled and deployed to a local sandbox for testing: ```bash cargo test ``` ```bash npm run test ````
Failing tests? Make sure that you are using `node v24 / 22 / 20`, and that you have installed `Rosetta` if you have a Mac with Apple Silicon
```bash uv run pytest ```
Feel free to check the test files to see how they interact with the contract. In short, a local NEAR sandbox is created, the contract is deployed, and different methods are called to verify the expected behavior. --- ## Build & Deploy the Contract Now that we know the tests are passing, let us deploy the contract! First, we need to compile it into WebAssembly: ```bash cargo near build non-reproducible-wasm ``` ```bash npm run build ``` ```bash # Build with nearc through the uv executor (no installation needed) uvx nearc contract.py ``` The above command will compile your Python contract into WebAssembly (WASM) that can be deployed to the NEAR blockchain. :::info The default `nearc` build configuration is appropriate for most contracts. You don't need to install nearc separately as we're using `uvx` to run it directly. ::: :::important This step requires [Emscripten](https://emscripten.org/) to be installed and accessible in your `PATH`. If you encounter errors during compilation, verify that Emscripten is properly installed with `emcc --version`. Common compilation errors and solutions: - `emcc: command not found` - Emscripten is not in your PATH. Run `source /path/to/emsdk/emsdk_env.sh` to add it temporarily. - `error: invalid version of emscripten` - Your Emscripten version might be too old. Try updating with `./emsdk install latest && ./emsdk activate latest`. - `Could not find platform micropython-dev-wasm32` - This typically means the Emscripten installation is incomplete or not properly activated. ::: ```bash near-go build ``` :::info Near Go SDK Build Process 1. All code from the main package, including imports from other modules, is combined into a single **generated_build.go** file. 2. The **generated_build.go** file is compiled into `wasm32-unknown-unknown` via **TinyGo**. ::: :::info Customizing the Build The default `near-go build` command works for most standard projects, compiling source code from the current directory into `main.wasm`. However, if you want to specify a custom output name or **inspect the intermediate glue code** (generated JSON serialization and SDK integration wrappers) for debugging purposes, you can use the available flags: ```bash near-go build --output my_contract.wasm --keep-generated ``` :::
### Create an Account Let us now create a NEAR account where we will deploy the contract: ```bash # Replace with a name for your contract account near create-account --useFaucet ``` :::tip Already have a testnet account? If you already have a `testnet` account and would like to use it instead, you can log in with the command `near login`. :::
Got an error on Windows? When working on `WSL` - or any other headless Linux environment - you might encounter issues when trying to create an account as the `cli` tries to save the keys into the system's keychain. In such cases, you can try the following command to create the account: ```bash near account create-account sponsor-by-faucet-service autogenerate-new-keypair save-to-legacy-keychain network-config testnet create ```

### Deploy it! With the contract ready, we can now deploy it to the `testnet` account we created earlier: ```bash near deploy ./target/near/auction.wasm ``` ```bash near deploy ./build/auction.wasm ``` ```bash near deploy ./auction.wasm ``` **Congrats!** Your contract now lives in the NEAR testnet network. --- ## Interacting with the Contract To interact with your deployed smart contract, you can call its functions through the command line. #### Initialize the Contract Let us initialize the auction by setting when it ends and who receives the funds (the auctioneer): ```bash # Get a timestamp for 5 minutes from now (in nanoseconds) FIVE_MINUTES_FROM_NOW=$(( $(date +%s%N) + 5 * 60 * 1000000000 )) # Initialize the auction near call init "{\"end_time\": \"$FIVE_MINUTES_FROM_NOW\", \"auctioneer\": \"influencer.testnet\"}" --useAccount ``` :::tip Feel free to replace `influencer.testnet` with any valid testnet account — this is where the winning bid will be sent :::
#### Place a Bid We can now place a bid in the auction by calling the `bid` method while attaching some NEAR deposit. On each bid, the highest bid and bidder information will be recorded in the contract's [storage](./anatomy/storage.md). ```bash # Create a new account to place the bid near create-account --useFaucet # Place a bid of 0.01 NEAR near call bid '{}' --deposit 0.01 --useAccount ``` :::note Note how in this case we are using the `` account (remember to rename it!) to call the `bid` function, while attaching a deposit of `0.01` NEAR as our bid :::
#### Get Highest Bid The `get_highest_bid` function only reads from the contract state, so it does not require a transaction or signature: ```bash near view get_highest_bid '{}' ```
Expected Output ```json { "bidder": "", "amount": "10000000000000000000000" } ```
:::tip Feel free to create as many bidder accounts as you want and place more bids to see how the highest bid changes! :::
#### Claim After the auction ends, anyone can call the `claim` method, which will transfer the `highest bid` amount to the auctioneer and end the auction. ```bash near call claim '{}' --useAccount ``` :::info Who won? After the auction ends, the highest bidder can be determined by simply calling the `get_highest_bid` method again ::: --- ## Common Questions ### What about mainnet? You can deploy a contract to mainnet using the same commands, just make sure to create a mainnet account and use `--networkId mainnet` flag in the `near` CLI commands. ### How much does it cost to deploy? The cost of deploying a contract depends on the contract's size, approximately 1Ⓝ ~100Kb. ### Can I update a contract after deploying? Yes. Redeploy with `near deploy `. The account stays the same, code updates. ### Which language should I choose? To build production apps prefer **Rust**, as it offers the most mature tooling and best performance. Otherwise, if you are just prototyping or learning, you can choose between your favorite language between **JavaScript**, **Python** or **Go**. ### How do I test without deploying? All languages support sandbox testing (shown in this guide). Tests run locally with a simulated NEAR environment. --- ## Moving Forward
Check the [auction frontend tutorial](../tutorials/auction/2.1-frontend.md) to learn how to build a simple web app that interacts with the auction contract
Follow the [auction NFT tutorial](../tutorials/auction/3.1-nft.md) to award the highest bidder a Non-Fungible Token (NFT) and allow users to bid using Fungible Tokens (FT)
Check our [Anatomy of a Contract](./anatomy/anatomy.md) page to understand the different components that make up a NEAR smart contract

Versioning for this article At the time of this writing, this example works with the following versions: - node: `22.18.0` - rustc: `1.86.0` - near-cli-rs: `0.22.0` - cargo-near: `0.16.1` - Python: `3.13` - near-sdk-py: `0.7.3` - uvx nearc: `0.9.2` - emscripten: `4.0.9` (required for Python contracts)
--- # Source: https://docs.near.org/smart-contracts/security/random.md --- id: random title: Random Numbers description: "Learn about secure random number generation in NEAR smart contracts and how to avoid predictable randomness vulnerabilities." --- When writing smart contracts in NEAR you have access to a `random seed` that enables you to create random numbers/strings within your contract. This `random seed` is **deterministic and verifiable**: it comes from the validator that produced the block signing the previous block-hash with their private key. The way the random seed is created implies two things: - Only the validator mining the transaction **can predict** which random number will come out. **No one else** could predict it because nobody knows the validator's private key (except the validator itself). - The validator **cannot interfere** with the random number being created. This is because they need to sign the previous block, over which (with a high probability) they had no control. However, notice that this still leaves room for three types of attacks from the validator: 1. [Frontrunning](./frontrunning.md), which we cover in another page 2. Gaming the input 3. Refusing to mine the block. ---- ## Gaming the Input Imagine you have a method that takes an input and gives a reward based on it. For example, you ask the user to choose a number, and if it the same as your `random seed` you give them money. Since the validator knows which `random seed` will come out, it can create a transaction with that specific input and win the prize. ---- ## Refusing to Mine the Block One way to fix the "gaming the input" problem is to force the user to send the input first, and then decide the result on a different block. Let's call these two stages: "bet" and "resolve". In this way, a validator cannot game the input, since the `random` number against which it will be compared is computed in a different block. However, something that the validator can still do to increase their chance of winning is: 1. Create a "bet" transaction with an account. 2. When it's their turn to validate, decide if they want to "resolve" or not. If the validator, on their turn, sees that generating a random number makes them win, they can add the transaction to the block. And if they see that they will not, they can skip the transaction. While this does not ensure that the validator will win (other good validators could mine the transaction), it can improve their chance of winning. Imagine a flip-coin game, where you choose `heads` or `tails` in the "bet" stage, and later resolve if you won or not. If you are a validator you can send a first transaction choosing either input. Then, on your turn to validate, you can check if your chosen input came out. If not, you can simply skip the transaction. This brings your probability of winning from `1/2` to `3/4`, that's a 25% increase! These odds, of course, dilute in games with more possible outcomes.
How does the math work here? Imagine you always bet for `heads`. In a fair coin-flip game you have 50-50 percent chance of winning, this is because after the coin is flipped there are two possible outcomes: `H` and `T`, and you only win in one (`H`). However, if you can choose to flip again if `tails` comes out, now there are 4 scenarios: `H H` `T H` `H T` `T T`, and in 3 of those you win (all the ones including an `H`)!!!.
--- # Source: https://docs.near.org/smart-contracts/anatomy/reduce-size.md --- id: reduce-size title: Reducing Contract Size description: "Learn strategies to reduce NEAR smart contract size for optimized deployment and performance." --- In this page, we will explore strategies for reducing the size of smart contracts on NEAR. This is particularly useful for developers who want to optimize their contracts for deployment, especially in scenarios where contract size limits are a concern. # Reducing a contract's size ## Advice & examples This page is made for developers familiar with lower-level concepts who wish to reduce their contract size significantly, perhaps at the expense of code readability. Some common scenarios where this approach may be helpful: - contracts intended to be tied to one's account management - contracts deployed using a factory - future advancements similar to the EVM on NEAR There have been a few items that may add unwanted bytes to a contract's size when compiled. Some of these may be more easily swapped for other approaches while others require more internal knowledge about system calls. ## Small wins ### Using flags When compiling a contract make sure to pass flag `-C link-arg=-s` to the rust compiler: ```bash RUSTFLAGS='-C link-arg=-s' cargo build --target wasm32-unknown-unknown --release ``` Here is the parameters we use for the most examples in `Cargo.toml`: ```toml [profile.release] codegen-units = 1 opt-level = "s" lto = true debug = false panic = "abort" overflow-checks = true ``` You may want to experiment with using `opt-level = "z"` instead of `opt-level = "s"` to see if generates a smaller binary. See more details on this in [The Cargo Book Profiles section](https://doc.rust-lang.org/cargo/reference/profiles.html#opt-level). You may also reference this [Shrinking .wasm Size](https://rustwasm.github.io/book/reference/code-size.html#tell-llvm-to-optimize-for-size-instead-of-speed) resource. ### Removing `rlib` from the manifest Ensure that your manifest (`Cargo.toml`) doesn't contain `rlib` unless it needs to. Some NEAR examples have included this: :::caution Adds unnecessary bloat ```toml [lib] crate-type = ["cdylib", "rlib"] ``` ::: when it could be: :::tip ```toml [lib] crate-type = ["cdylib"] ``` ::: 3. When using the Rust SDK, you may override the default JSON serialization to use [Borsh](https://borsh.io) instead. [See this page](./serialization-interface.md#overriding-serialization-protocol-default) for more information and an example. 4. When using assertions or guards, avoid using the standard `assert` macros like [`assert!`](https://doc.rust-lang.org/std/macro.assert.html), [`assert_eq!`](https://doc.rust-lang.org/std/macro.assert_eq.html), or [`assert_ne!`](https://doc.rust-lang.org/std/macro.assert_ne.html) as these may add bloat for information regarding the line number of the error. There are similar issues with `unwrap`, `expect`, and Rust's `panic!()` macro. Example of a standard assertion: :::caution Adds unnecessary bloat ```rust assert_eq!(contract_owner, predecessor_account, "ERR_NOT_OWNER"); ``` ::: when it could be: :::tip ```rust if contract_owner != predecessor_account { env::panic(b"ERR_NOT_OWNER"); } ``` ::: Example of removing `expect`: :::caution Adds unnecessary bloat ```rust let owner_id = self.owner_by_id.get(&token_id).expect("Token not found"); ``` ::: when it could be: :::tip ```rust fn expect_token_found(option: Option) -> T { option.unwrap_or_else(|| env::panic_str("Token not found")) } let owner_id = expect_token_found(self.owner_by_id.get(&token_id)); ``` ::: Example of changing standard `panic!()`: :::caution Adds unnecessary bloat ```rust panic!("ERR_MSG_HERE"); ``` ::: when it could be: :::tip ```rust env::panic_str("ERR_MSG_HERE"); ``` ::: ## Ready to use script We have prepared a simple `bash` script that can be used to minify `.wasm` contract file. You can find it [here](https://github.com/near/near-sdk-rs/blob/master/minifier/minify.sh). The current approach to minification is the following: 1. Snip (i.e. just replace with unreachable instruction) few known fat functions from the standard library (such as float formatting and panic-related) with `wasm-snip`. 2. Run `wasm-gc` to eliminate all functions reachable from the snipped functions. 3. Strip unneeded sections, such as names with `wasm-strip`. 4. Run `binaryen wasm-opt`, which cleans up the rest. ### Requirements to run the script: - install [wasm-snip](https://docs.rs/wasm-snip/0.4.0/wasm_snip/) and [wasm-gc](https://docs.rs/crate/wasm-gc/0.1.6) with Cargo: ```bash cargo install wasm-snip wasm-gc ``` - install [binaryen](https://github.com/WebAssembly/binaryen) and [wabt](https://github.com/WebAssembly/wabt) on your system. For Ubuntu and other Debian based Linux distributions run: ```bash apt install binaryen wabt ``` :::danger Minification could be rather aggressive, so you must test the contract after minification. Standalone NEAR runtime could be helpful [here](https://github.com/nearprotocol/nearcore/tree/master/runtime/near-vm-runner). ::: ## Lower-level approach For a `no_std` approach to minimal contracts, observe the following examples: - [Tiny contract](https://github.com/near/nearcore/tree/1e7c6613f65c23f87adf2c92e3d877f4ffe666ea/runtime/near-test-contracts/tiny-contract-rs) - [NEAR ETH Gateway](https://github.com/ilblackdragon/near-eth-gateway/blob/master/proxy/src/lib.rs) - [This YouTube video](https://youtu.be/Hy4VBSCqnsE) where Eugene demonstrates a fungible token in `no_std` mode. The code for this [example lives here](https://github.com/near/core-contracts/pull/88). - [Examples using a project called `nesdie`](https://github.com/austinabell/nesdie/tree/main/examples). - Note that Aurora has found success using [rjson](https://crates.io/crates/rjson) as a lightweight JSON serialization crate. It has a smaller footprint than [serde](https://crates.io/crates/serde) which is currently packaged with the Rust SDK. See [this example of rjson](https://github.com/aurora-is-near/aurora-engine/blob/65a1d11fcd16192cc1bda886c62005c603189a24/src/json.rs#L254) in an Aurora repository, although implementation details will have to be gleaned by the reader and won't be expanded upon here. [This nesdie example](https://github.com/austinabell/nesdie/blob/bb6beb77e32cd54077ac54bf028f262a9dfb6ad0/examples/multisig/src/utils/json/vector.rs#L26-L30) also uses the [miniserde crate](https://crates.io/crates/miniserde), which is another option to consider for folks who choose to avoid using the Rust SDK. :::note Information on system calls
Expand to see what's available from sys.rs ``` //! Blockchain-specific methods available to the smart contract that allow to interact with NEAR runtime. //! This is a wrapper around a low-level [`near_sys`](near_sys). //! //! Unless you know what you are doing prefer using `env::*` //! whenever possible. //! //! In case of cross-contract calls prefer using higher-level API available //! through [`crate::Promise`], and [`crate::PromiseOrValue`]. use std::convert::TryFrom; use std::convert::TryInto; use std::mem::{size_of, size_of_val}; use std::panic as std_panic; #[cfg(all(not(target_arch = "wasm32"), feature = "unit-testing"))] use crate::mock::MockedBlockchain; use crate::promise::Allowance; #[cfg(feature = "global-contracts")] use crate::types::AccountIdRef; use crate::types::{ AccountId, BlockHeight, Gas, NearToken, PromiseIndex, PromiseResult, PublicKey, StorageUsage, }; use crate::{CryptoHash, GasWeight, PromiseError}; #[cfg(feature = "deterministic-account-ids")] use crate::{AccountContract, ActionIndex, GlobalContractId}; use near_sys as sys; const REGISTER_EXPECTED_ERR: &str = "Register was expected to have data because we just wrote it into it."; /// Register used internally for atomic operations. This register is safe to use by the user, /// since it only needs to be untouched while methods of `Environment` execute, which is guaranteed /// guest code is not parallel. const ATOMIC_OP_REGISTER: u64 = u64::MAX - 2; /// Register used to record evicted values from the storage. const EVICTED_REGISTER: u64 = u64::MAX - 1; /// Key used to store the state of the contract. const STATE_KEY: &[u8] = b"STATE"; /// The minimum length of a valid account ID. const MIN_ACCOUNT_ID_LEN: u64 = 2; /// The maximum length of a valid account ID. const MAX_ACCOUNT_ID_LEN: u64 = 64; #[inline] #[track_caller] fn expect_register(option: Option) -> T { option.unwrap_or_else(|| panic_str(REGISTER_EXPECTED_ERR)) } /// A simple helper to read blob value coming from host's method. #[inline] fn try_method_into_register(method: unsafe extern "C" fn(u64)) -> Option> { unsafe { method(ATOMIC_OP_REGISTER) }; read_register(ATOMIC_OP_REGISTER) } /// Same as `try_method_into_register` but expects the data. #[inline] #[track_caller] fn method_into_register(method: unsafe extern "C" fn(u64)) -> Vec { expect_register(try_method_into_register(method)) } #[inline] pub(crate) unsafe fn read_register_fixed(register_id: u64) -> [u8; N] { let mut buf = [0; N]; sys::read_register(register_id, buf.as_mut_ptr() as _); buf } /// Replaces the current low-level blockchain interface accessible through `env::*` with another /// low-level blockchain interface with builtin functions of the NEAR runtime. In most cases you /// want to use `testing_env!` macro to set it. /// /// ``` /// # let context = near_sdk::test_utils::VMContextBuilder::new().build(); /// # let vm_config = near_sdk::test_vm_config(); /// # let fees_config = near_sdk::RuntimeFeesConfig::test(); /// # let storage = Default::default(); /// # let validators = Default::default(); /// let mocked_blockchain = near_sdk::MockedBlockchain::new( /// context, /// vm_config, /// fees_config, /// vec![], /// storage, /// validators, /// None, /// ); /// near_sdk::env::set_blockchain_interface(mocked_blockchain); /// ``` #[cfg(all(not(target_arch = "wasm32"), feature = "unit-testing"))] pub fn set_blockchain_interface(blockchain_interface: MockedBlockchain) { crate::mock::with_mocked_blockchain(|b| { *b = blockchain_interface; }) } /// Implements panic hook that converts `PanicInfo` into a string and provides it through the /// blockchain interface. fn panic_hook_impl(info: &std_panic::PanicHookInfo) { panic_str(info.to_string().as_str()); } /// Setups panic hook to expose error info to the blockchain. pub fn setup_panic_hook() { std_panic::set_hook(Box::new(panic_hook_impl)); } /// Reads the content of the `register_id`. If register is not used returns `None`. pub fn read_register(register_id: u64) -> Option> { read_register_bounded(register_id, usize::MAX).map(|r| { r // vector can't be longer than usize::MAX .unwrap_or_else(|_| unreachable!()) }) } /// Same as [`read_register`] but bounded: if data in the register is /// longer than `max_len`, then `Some(Err(len))` is returned. pub fn read_register_bounded(register_id: u64, max_len: usize) -> Option, usize>> { // Get register length and convert to a usize. The max register size in config is much less // than the u32 max so the abort should never be hit, but is there for safety because there // would be undefined behaviour during `read_register` if the buffer length is truncated. let len: usize = register_len(register_id)?.try_into().unwrap_or_else(|_| abort()); if len > max_len { return Some(Err(len)); } // Initialize buffer with capacity. let mut buffer = Vec::with_capacity(len); // Read register into buffer. //* SAFETY: This is safe because the buffer is initialized with the exact capacity of the //* register that is being read from. unsafe { sys::read_register(register_id, buffer.as_mut_ptr() as u64); // Set updated length after writing to buffer. buffer.set_len(len); } Some(Ok(buffer)) } /// Returns the size of the register. If register is not used returns `None`. pub fn register_len(register_id: u64) -> Option { let len = unsafe { sys::register_len(register_id) }; if len == u64::MAX { None } else { Some(len) } } macro_rules! maybe_cached { ($t:ty: $v:block) => {{ #[cfg(not(feature = "unit-testing"))] { static CACHED: ::std::sync::LazyLock<$t> = ::std::sync::LazyLock::new(|| $v); CACHED.clone() } #[cfg(feature = "unit-testing")] $v }}; } // ############### // # Context API # // ############### /// The id of the account that owns the current contract. /// /// # Examples /// ``` /// use near_sdk::env::current_account_id; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// assert_eq!(current_account_id(), "alice.near".parse::().unwrap()); /// ``` pub fn current_account_id() -> AccountId { maybe_cached!(AccountId: { assert_valid_account_id(method_into_register(sys::current_account_id)) }) } /// The code of the current contract. /// /// # Examples /// ```no_run /// use near_sdk::env::current_contract_code; /// use near_sdk::AccountContract; /// /// assert!(matches!(current_contract_code(), AccountContract::Local(_))); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn current_contract_code() -> AccountContract { maybe_cached!(AccountContract: { let mode = unsafe { sys::current_contract_code(ATOMIC_OP_REGISTER) }; match mode { 0 => AccountContract::None, 1 => AccountContract::Local(unsafe { read_register_fixed(ATOMIC_OP_REGISTER) }.into()), 2 => AccountContract::Global(unsafe { read_register_fixed(ATOMIC_OP_REGISTER) }.into()), 3 => AccountContract::GlobalByAccount(assert_valid_account_id(method_into_register( sys::current_account_id, ))), _ => panic!("Invalid contract mode"), } }) } /// Returns global contract identifier of the contract's code currently being /// executed. Otherwise, returns `None` if the current contract is not using /// globally deployed code. /// /// # Examples /// ```no_run /// use near_sdk::env::current_global_contract_id; /// use near_sdk::GlobalContractId; /// /// assert!(matches!(current_global_contract_id(), Some(GlobalContractId::CodeHash(_)))); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn current_global_contract_id() -> Option { Some(match current_contract_code() { AccountContract::Global(hash) => GlobalContractId::CodeHash(hash), AccountContract::GlobalByAccount(account_id) => GlobalContractId::AccountId(account_id), _ => return None, }) } /// The account id that will receive the refund if the contract panics. /// /// # Examples /// ``` /// use near_sdk::env::refund_to_account_id; /// /// assert_eq!(refund_to_account_id(), "bob.near".parse::().unwrap()); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn refund_to_account_id() -> AccountId { maybe_cached!(AccountId: { assert_valid_account_id(method_into_register(sys::refund_to_account_id)) }) } /// The id of the account that either signed the original transaction or issued the initial /// cross-contract call. /// /// # Examples /// ``` /// use near_sdk::env::signer_account_id; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// assert_eq!(signer_account_id(), "bob.near".parse::().unwrap()); /// ``` pub fn signer_account_id() -> AccountId { maybe_cached!(AccountId: { assert_valid_account_id(method_into_register(sys::signer_account_id)) }) } /// The public key of the account that did the signing. /// /// # Examples /// ``` /// use near_sdk::env::signer_account_pk; /// use near_sdk::{PublicKey, CurveType}; /// /// let pk = PublicKey::from_parts(near_sdk::CurveType::ED25519, vec![0; 32]).unwrap(); /// assert_eq!(signer_account_pk(), pk); /// ``` pub fn signer_account_pk() -> PublicKey { maybe_cached!(PublicKey: { PublicKey::try_from(method_into_register(sys::signer_account_pk)) .unwrap_or_else(|_| abort()) }) } /// The id of the account that was the previous contract in the chain of cross-contract calls. /// If this is the first contract, it is equal to `signer_account_id`. /// /// # Examples /// ``` /// use near_sdk::env::predecessor_account_id; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// assert_eq!(predecessor_account_id(), "bob.near".parse::().unwrap()); /// ``` pub fn predecessor_account_id() -> AccountId { maybe_cached!(AccountId: { assert_valid_account_id(method_into_register(sys::predecessor_account_id)) }) } /// Helper function to convert and check the account ID from bytes from the runtime. fn assert_valid_account_id(bytes: Vec) -> AccountId { String::from_utf8(bytes) .ok() .and_then(|s| AccountId::try_from(s).ok()) .unwrap_or_else(|| abort()) } /// The input to the contract call serialized as bytes. If input is not provided returns `None`. /// /// # Examples /// ``` /// use near_sdk::env::input; /// /// assert_eq!(input(), Some(Vec::new())); /// ``` /// See an example here [here](https://github.com/near-examples/update-migrate-rust/blob/a1a326de73c152831f93fbf6d90932e13a08b89f/self-updates/update/src/update.rs#L19) pub fn input() -> Option> { try_method_into_register(sys::input) } /// Current block index. /// /// # Examples /// ``` /// use near_sdk::env::block_index; /// /// assert_eq!(block_index(), 0); /// ``` #[deprecated(since = "4.0.0", note = "Use block_height instead")] pub fn block_index() -> BlockHeight { block_height() } /// Returns the height of the block the transaction is being executed in. /// /// # Examples /// ``` /// use near_sdk::env::block_height; /// /// assert_eq!(block_height(), 0); /// ``` pub fn block_height() -> BlockHeight { maybe_cached!(BlockHeight: { unsafe { sys::block_height() } }) } /// Current block timestamp, i.e, number of non-leap-nanoseconds since January 1, 1970 0:00:00 UTC. /// /// # Examples /// ``` /// use near_sdk::env::block_timestamp; /// /// assert_eq!(block_timestamp(), 0); /// ``` pub fn block_timestamp() -> u64 { maybe_cached!(u64: { unsafe { sys::block_timestamp() } }) } /// Current block timestamp, i.e, number of non-leap-milliseconds since January 1, 1970 0:00:00 UTC. /// /// # Examples /// ``` /// use near_sdk::env::block_timestamp_ms; /// /// assert_eq!(block_timestamp_ms(), 0); /// ``` pub fn block_timestamp_ms() -> u64 { block_timestamp() / 1_000_000 } /// Current epoch height. /// /// # Examples /// ``` /// use near_sdk::env::epoch_height; /// /// assert_eq!(epoch_height(), 0); /// ``` pub fn epoch_height() -> u64 { maybe_cached!(u64: { unsafe { sys::epoch_height() } }) } /// Current total storage usage of this smart contract that this account would be paying for. /// /// # Examples /// ``` /// use near_sdk::env::storage_usage; /// /// assert_eq!(storage_usage(), 307200); /// ``` pub fn storage_usage() -> StorageUsage { unsafe { sys::storage_usage() } } // ################# // # Economics API # // ################# /// The balance attached to the given account. This includes the attached_deposit that was /// attached to the transaction /// /// # Examples /// ``` /// use near_sdk::env::account_balance; /// use near_sdk::NearToken; /// /// assert_eq!(account_balance(), NearToken::from_near(100)); /// ``` pub fn account_balance() -> NearToken { let mut data = [0u8; size_of::()]; unsafe { sys::account_balance(data.as_mut_ptr() as u64) }; NearToken::from_yoctonear(u128::from_le_bytes(data)) } /// The balance locked for potential validator staking. /// /// # Examples /// ``` /// use near_sdk::env::account_locked_balance; /// use near_sdk::NearToken; /// /// assert_eq!(account_locked_balance(), NearToken::from_yoctonear(0)); /// ``` pub fn account_locked_balance() -> NearToken { let mut data = [0u8; size_of::()]; unsafe { sys::account_locked_balance(data.as_mut_ptr() as u64) }; NearToken::from_yoctonear(u128::from_le_bytes(data)) } /// The balance that was attached to the call that will be immediately deposited before the /// contract execution starts /// /// # Examples /// ``` /// use near_sdk::env::attached_deposit; /// use near_sdk::NearToken; /// /// assert_eq!(attached_deposit(), NearToken::from_yoctonear(0)); /// ``` pub fn attached_deposit() -> NearToken { maybe_cached!(NearToken: { let mut data = [0u8; size_of::()]; unsafe { sys::attached_deposit(data.as_mut_ptr() as u64) }; NearToken::from_yoctonear(u128::from_le_bytes(data)) }) } /// The amount of gas attached to the call that can be used to pay for the gas fees. /// /// # Examples /// ``` /// use near_sdk::env::prepaid_gas; /// use near_sdk::Gas; /// /// assert_eq!(prepaid_gas(), Gas::from_tgas(300)); /// ``` pub fn prepaid_gas() -> Gas { maybe_cached!(Gas: { Gas::from_gas(unsafe { sys::prepaid_gas() }) }) } /// The gas that was already burnt during the contract execution (cannot exceed `prepaid_gas`) /// /// # Examples /// ``` /// use near_sdk::env::used_gas; /// use near_sdk::Gas; /// /// assert_eq!(used_gas(), Gas::from_gas(264768111)); /// ``` pub fn used_gas() -> Gas { Gas::from_gas(unsafe { sys::used_gas() }) } // ############ // # Math API # // ############ /// Returns the random seed from the current block. This 32 byte hash is based on the VRF value from /// the block. This value is not modified in any way each time this function is called within the /// same method/block. /// /// # Examples /// ``` /// use near_sdk::env::random_seed; /// /// assert_eq!(random_seed(), vec![0; 32]); /// ``` pub fn random_seed() -> Vec { random_seed_array().to_vec() } /// Returns the random seed from the current block. This 32 byte hash is based on the VRF value from /// the block. This value is not modified in any way each time this function is called within the /// same method/block. /// Example of usage: /// ```rust /// use rand::{Rng, SeedableRng}; /// use rand_chacha::ChaCha20Rng; /// use near_sdk::near; /// use near_sdk::env; /// #[near(contract_state)] /// struct RngExample { /// val: i32, /// } /// #[near] /// impl RngExample { /// pub fn increment(&mut self) { /// let mut rng = ChaCha20Rng::from_seed(env::random_seed_array()); /// let value = rng.gen_range(0..1011); /// self.val += value; /// } /// pub fn get_value(&mut self) -> i32 { /// self.val /// } /// } /// ``` /// /// Example of usage with [near-rng](https://lib.rs/crates/near-rng) which allows to decrease size of contract binary: /// /// ```rust /// use near_rng::Rng; /// use near_sdk::near; /// use near_sdk::env; /// #[near(contract_state)] /// struct NearRngExample { /// val: i32, /// } /// #[near] /// impl NearRngExample { /// pub fn increment(&mut self) { /// let mut rng = Rng::new(&env::random_seed()); /// let value = rng.rand_range_i32(0, 20); /// self.val += value; /// } /// pub fn get_value(&mut self) -> i32 { /// self.val /// } /// } /// ``` /// More info in [documentation](https://docs.near.org/develop/contracts/security/random) pub fn random_seed_array() -> [u8; 32] { maybe_cached!([u8; 32]: { //* SAFETY: random_seed syscall will always generate 32 bytes inside of the atomic op register //* so the read will have a sufficient buffer of 32, and can transmute from uninit //* because all bytes are filled. This assumes a valid random_seed implementation. unsafe { sys::random_seed(ATOMIC_OP_REGISTER); read_register_fixed(ATOMIC_OP_REGISTER) } }) } /// Hashes the random sequence of bytes using sha256. /// /// # Examples /// ``` /// use near_sdk::env::sha256; /// use hex; /// /// assert_eq!( /// sha256(b"The phrase that will be hashed"), /// hex::decode("7fc38bc74a0d0e592d2b8381839adc2649007d5bca11f92eeddef78681b4e3a3").expect("Decoding failed") /// ); /// ``` pub fn sha256(value: impl AsRef<[u8]>) -> Vec { sha256_array(value.as_ref()).to_vec() } /// Hashes the random sequence of bytes using keccak256. /// /// # Examples /// ``` /// use near_sdk::env::keccak256; /// use hex; /// /// assert_eq!( /// keccak256(b"The phrase that will be hashed"), /// hex::decode("b244af9dd4aada2eda59130bbcff112f29b427d924b654aaeb5a0384fa9afed4") /// .expect("Decoding failed") /// ); /// ``` pub fn keccak256(value: impl AsRef<[u8]>) -> Vec { keccak256_array(value.as_ref()).to_vec() } /// Hashes the random sequence of bytes using keccak512. /// /// # Examples /// ``` /// use near_sdk::env::keccak512; /// use hex; /// /// assert_eq!( /// keccak512(b"The phrase that will be hashed"), /// hex::decode("29a7df7b889a443fdfbd769adb57ef7e98e6159187b582baba778c06e8b41a75f61367257e8c525a95b3f13ddf432f115d1df128a910c8fc93221db136d92b31") /// .expect("Decoding failed") /// ); /// ``` pub fn keccak512(value: impl AsRef<[u8]>) -> Vec { keccak512_array(value.as_ref()).to_vec() } /// Hashes the bytes using the SHA-256 hash function. This returns a 32 byte hash. /// /// # Examples /// ``` /// use near_sdk::env::sha256_array; /// use hex; /// /// assert_eq!( /// &sha256_array(b"The phrase that will be hashed"), /// hex::decode("7fc38bc74a0d0e592d2b8381839adc2649007d5bca11f92eeddef78681b4e3a3") /// .expect("Decoding failed") /// .as_slice() /// ); /// ``` pub fn sha256_array(value: impl AsRef<[u8]>) -> CryptoHash { let value = value.as_ref(); //* SAFETY: sha256 syscall will always generate 32 bytes inside of the atomic op register //* so the read will have a sufficient buffer of 32, and can transmute from uninit //* because all bytes are filled. This assumes a valid sha256 implementation. unsafe { sys::sha256(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); read_register_fixed(ATOMIC_OP_REGISTER) } } /// Hashes the bytes using the Keccak-256 hash function. This returns a 32 byte hash. /// /// # Examples /// ``` /// use near_sdk::env::keccak256_array; /// use hex; /// /// assert_eq!( /// &keccak256_array(b"The phrase that will be hashed"), /// hex::decode("b244af9dd4aada2eda59130bbcff112f29b427d924b654aaeb5a0384fa9afed4") /// .expect("Decoding failed") /// .as_slice() /// ); /// ``` pub fn keccak256_array(value: impl AsRef<[u8]>) -> CryptoHash { let value = value.as_ref(); //* SAFETY: keccak256 syscall will always generate 32 bytes inside of the atomic op register //* so the read will have a sufficient buffer of 32, and can transmute from uninit //* because all bytes are filled. This assumes a valid keccak256 implementation. unsafe { sys::keccak256(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); read_register_fixed(ATOMIC_OP_REGISTER) } } /// Hashes the bytes using the Keccak-512 hash function. This returns a 64 byte hash. /// /// # Examples /// ``` /// use near_sdk::env::keccak512_array; /// use hex; /// /// assert_eq!( /// &keccak512_array(b"The phrase that will be hashed"), /// hex::decode("29a7df7b889a443fdfbd769adb57ef7e98e6159187b582baba778c06e8b41a75f61367257e8c525a95b3f13ddf432f115d1df128a910c8fc93221db136d92b31") /// .expect("Decoding failed") /// .as_slice() /// ); /// ``` pub fn keccak512_array(value: impl AsRef<[u8]>) -> [u8; 64] { let value = value.as_ref(); //* SAFETY: keccak512 syscall will always generate 64 bytes inside of the atomic op register //* so the read will have a sufficient buffer of 64, and can transmute from uninit //* because all bytes are filled. This assumes a valid keccak512 implementation. unsafe { sys::keccak512(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); read_register_fixed(ATOMIC_OP_REGISTER) } } /// Hashes the bytes using the RIPEMD-160 hash function. This returns a 20 byte hash. /// /// # Examples /// ``` /// use near_sdk::env::ripemd160_array; /// use hex; /// /// assert_eq!( /// &ripemd160_array(b"The phrase that will be hashed"), /// hex::decode("9a48b9195fcb14cfe6051c0a1be7882efcadaed8") /// .expect("Decoding failed") /// .as_slice() /// ); /// ``` pub fn ripemd160_array(value: impl AsRef<[u8]>) -> [u8; 20] { let value = value.as_ref(); //* SAFETY: ripemd160 syscall will always generate 20 bytes inside of the atomic op register //* so the read will have a sufficient buffer of 20, and can transmute from uninit //* because all bytes are filled. This assumes a valid ripemd160 implementation. unsafe { sys::ripemd160(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); read_register_fixed(ATOMIC_OP_REGISTER) } } /// Recovers an ECDSA signer address from a 32-byte message `hash` and a corresponding `signature` /// along with `v` recovery byte. /// /// Takes in an additional flag to check for malleability of the signature /// which is generally only ideal for transactions. /// /// Returns 64 bytes representing the public key if the recovery was successful. #[cfg(feature = "unstable")] pub fn ecrecover( hash: &[u8], signature: &[u8], v: u8, malleability_flag: bool, ) -> Option<[u8; 64]> { unsafe { let return_code = sys::ecrecover( hash.len() as _, hash.as_ptr() as _, signature.len() as _, signature.as_ptr() as _, v as u64, malleability_flag as u64, ATOMIC_OP_REGISTER, ); if return_code == 0 { None } else { Some(read_register_fixed(ATOMIC_OP_REGISTER)) } } } /// Verifies signature of message using provided ED25519 Public Key /// /// # Examples /// ``` /// use near_sdk::env::ed25519_verify; /// use hex; /// /// assert_eq!( /// ed25519_verify( /// hex::decode("41C44494DAB13009BE73D2CCBD3A49677DDC1F26AD2823CE72833CE4B9603F77CA70A9E179272D92D28E8B2AE7006747C87AB1890362A50347EFF553F5EC4008") /// .expect("Decoding failed") /// .as_slice() /// .try_into() /// .unwrap(), /// b"Hello world!", /// hex::decode("9C16937BF04CCE709FED52344C43634F1E7A05FC29DD41F48844C3588C7FE663") /// .expect("Decoding failed") /// .as_slice() /// .try_into() /// .unwrap(), /// ), /// true /// ); /// /// assert_eq!( /// ed25519_verify( /// hex::decode("41C44494DAB13009BE73D2CCBD3A49677DDC1F26AD2823CE72833CE4B9603F77CA70A9E179272D92D28E8B2AE7006747C87AB1890362A50347EFF553F5EC4008") /// .expect("Decoding failed") /// .as_slice() /// .try_into() /// .unwrap(), /// b"Modified message!", /// hex::decode("9C16937BF04CCE709FED52344C43634F1E7A05FC29DD41F48844C3588C7FE663") /// .expect("Decoding failed") /// .as_slice() /// .try_into() /// .unwrap(), /// ), /// false /// ); /// ``` pub fn ed25519_verify( signature: &[u8; 64], message: impl AsRef<[u8]>, public_key: &[u8; 32], ) -> bool { let message = message.as_ref(); unsafe { sys::ed25519_verify( signature.len() as _, signature.as_ptr() as _, message.len() as _, message.as_ptr() as _, public_key.len() as _, public_key.as_ptr() as _, ) == 1 } } /// Compute alt_bn128 g1 multiexp. /// /// `alt_bn128` is a specific curve from the Barreto-Naehrig(BN) family. It is particularly /// well-suited for ZK proofs. /// /// See also: [EIP-196](https://eips.ethereum.org/EIPS/eip-196) pub fn alt_bn128_g1_multiexp(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::alt_bn128_g1_multiexp(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Compute alt_bn128 g1 sum. /// /// `alt_bn128` is a specific curve from the Barreto-Naehrig(BN) family. It is particularly /// well-suited for ZK proofs. /// /// See also: [EIP-196](https://eips.ethereum.org/EIPS/eip-196) pub fn alt_bn128_g1_sum(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::alt_bn128_g1_sum(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Compute pairing check /// /// `alt_bn128` is a specific curve from the Barreto-Naehrig(BN) family. It is particularly /// well-suited for ZK proofs. /// /// See also: [EIP-197](https://eips.ethereum.org/EIPS/eip-197) pub fn alt_bn128_pairing_check(value: impl AsRef<[u8]>) -> bool { let value = value.as_ref(); unsafe { sys::alt_bn128_pairing_check(value.len() as _, value.as_ptr() as _) == 1 } } // ############# // # BLS12-381 # // ############# /// Compute BLS12-381 G1 sum. /// /// See also: [IETF draft-irtf-cfrg-pairing-friendly-curves](https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-pairing-friendly-curves) pub fn bls12381_p1_sum(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_p1_sum(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Compute BLS12-381 G2 sum. pub fn bls12381_p2_sum(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_p2_sum(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Compute BLS12-381 G1 multiexponentiation. pub fn bls12381_g1_multiexp(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_g1_multiexp(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Compute BLS12-381 G2 multiexponentiation. pub fn bls12381_g2_multiexp(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_g2_multiexp(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Map an Fp element to a BLS12-381 G1 point. pub fn bls12381_map_fp_to_g1(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_map_fp_to_g1(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Map an Fp2 element to a BLS12-381 G2 point. pub fn bls12381_map_fp2_to_g2(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_map_fp2_to_g2(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Perform BLS12-381 pairing check. Returns true if the pairing check passes. pub fn bls12381_pairing_check(value: impl AsRef<[u8]>) -> bool { let value = value.as_ref(); unsafe { sys::bls12381_pairing_check(value.len() as _, value.as_ptr() as _) == 0 } } /// Decompress a BLS12-381 G1 point. pub fn bls12381_p1_decompress(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_p1_decompress(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } /// Decompress a BLS12-381 G2 point. pub fn bls12381_p2_decompress(value: impl AsRef<[u8]>) -> Vec { let value = value.as_ref(); unsafe { sys::bls12381_p2_decompress(value.len() as _, value.as_ptr() as _, ATOMIC_OP_REGISTER); }; match read_register(ATOMIC_OP_REGISTER) { Some(result) => result, None => panic_str(REGISTER_EXPECTED_ERR), } } // ################ // # Promises API # // ################ /// Creates a promise that will execute a method on account with given arguments and attaches /// the given amount and gas. /// /// # Examples /// ``` /// use near_sdk::env::promise_create; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas}; /// use std::str::FromStr; /// /// let promise = promise_create( /// "counter.near".parse::().unwrap(), /// "increment", /// serde_json::json!({ /// "value": 5 /// }).to_string(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30) /// ); /// ``` /// /// More info about promises in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/crosscontract#promises) /// /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_create`] /// /// Example usages of this low-level api are and /// pub fn promise_create( account_id: AccountId, function_name: &str, arguments: impl AsRef<[u8]>, amount: NearToken, gas: Gas, ) -> PromiseIndex { let account_id = account_id.as_bytes(); let arguments = arguments.as_ref(); unsafe { PromiseIndex(sys::promise_create( account_id.len() as _, account_id.as_ptr() as _, function_name.len() as _, function_name.as_ptr() as _, arguments.len() as _, arguments.as_ptr() as _, &amount.as_yoctonear() as *const u128 as _, gas.as_gas(), )) } } /// Attaches the callback (which is a [`near_primitives::action::FunctionCallAction`]) that is executed after promise pointed by `promise_idx` is complete. /// /// # Examples /// ``` /// use near_sdk::env::{promise_create, promise_then}; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas}; /// use std::str::FromStr; /// /// let promise = promise_create( /// "counter.near".parse().unwrap(), /// "increment", /// serde_json::json!({ /// "value": 5 /// }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30) /// ); /// /// let chained_promise = promise_then( /// promise, /// "greetings.near".parse().unwrap(), /// "set_greeting", /// serde_json::json!({ /// "text": "Hello World" /// }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(4000000000000), /// Gas::from_tgas(30) /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_then`] /// /// Example usages of this low-level api are and pub fn promise_then( promise_idx: PromiseIndex, account_id: AccountId, function_name: &str, arguments: impl AsRef<[u8]>, amount: NearToken, gas: Gas, ) -> PromiseIndex { let account_id = account_id.as_bytes(); let arguments = arguments.as_ref(); unsafe { PromiseIndex(sys::promise_then( promise_idx.0, account_id.len() as _, account_id.as_ptr() as _, function_name.len() as _, function_name.as_ptr() as _, arguments.len() as _, arguments.as_ptr() as _, &amount.as_yoctonear() as *const u128 as _, gas.as_gas(), )) } } /// Creates a new promise which completes when time all promises passed as arguments complete. /// /// # Examples /// ``` /// use near_sdk::env::{promise_create, promise_and}; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas}; /// use std::str::FromStr; /// /// let promise1 = promise_create( /// "counter.near".parse().unwrap(), /// "increment", /// serde_json::json!({ /// "value": 5 /// }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30) /// ); /// /// let promise2 = promise_create( /// "greetings.near".parse().unwrap(), /// "set_greeting", /// serde_json::json!({ /// "text": "Hello World" /// }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(4000000000000), /// Gas::from_tgas(30) /// ); /// /// let chained_promise = promise_and(&[promise1, promise2]); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_and`] pub fn promise_and(promise_indices: &[PromiseIndex]) -> PromiseIndex { let mut data = vec![0u8; size_of_val(promise_indices)]; for i in 0..promise_indices.len() { data[i * size_of::()..(i + 1) * size_of::()] .copy_from_slice(&promise_indices[i].0.to_le_bytes()); } unsafe { PromiseIndex(sys::promise_and(data.as_ptr() as _, promise_indices.len() as _)) } } /// # Examples /// ```no_run /// /// use near_sdk::env; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// let promise = env::promise_batch_create( /// &"receiver.near".parse().unwrap() /// ); /// ``` /// Create a NEAR promise which will have multiple promise actions inside. /// /// Example: /// ```no_run /// use near_sdk::{env, NearToken, Gas, AccountId}; /// /// let promise_index = env::promise_batch_create( /// &"example.near".parse().unwrap() /// ); /// /// // Adding actions to the promise /// env::promise_batch_action_transfer(promise_index, NearToken::from_near(10u128)); // Transfer 10 NEAR /// env::promise_batch_action_function_call( /// promise_index, /// "method_name", // Target method /// b"{}", // Arguments /// NearToken::from_near(0), // Attached deposit /// Gas::from_tgas(5) // Gas for execution /// ); /// ``` /// All actions in a batch are executed in the order they were added. /// Batched actions act as a unit: they execute in the same receipt, and if any fails, then they all get reverted. /// More information about batching actions can be found in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/actions) /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_create`] /// See example of usage [here](https://github.com/near/near-sdk-rs/blob/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_batch_create(account_id: &AccountId) -> PromiseIndex { let account_id: &str = account_id.as_ref(); unsafe { PromiseIndex(sys::promise_batch_create(account_id.len() as _, account_id.as_ptr() as _)) } } /// # Examples /// ``` /// use near_sdk::env::{promise_batch_then, promise_create}; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas}; /// use std::str::FromStr; /// /// let promise = promise_create( /// "counter.near".parse().unwrap(), /// "increment", /// serde_json::json!({ /// "value": 5 /// }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30) /// ); /// /// let new_promise = promise_batch_then( /// promise, /// &"receiver.near".parse().unwrap() /// ); /// ``` /// Attach a callback NEAR promise to a batch of NEAR promise actions. /// /// More info about batching [here](crate::env::promise_batch_create) /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_then`] pub fn promise_batch_then(promise_index: PromiseIndex, account_id: &AccountId) -> PromiseIndex { let account_id: &str = account_id.as_ref(); unsafe { PromiseIndex(sys::promise_batch_then( promise_index.0, account_id.len() as _, account_id.as_ptr() as _, )) } } /// Set the account id that will receive the refund if the promise panics. /// Uses low-level [`crate::sys::promise_set_refund_to`] /// /// # Examples /// ``` /// use near_sdk::env::{promise_set_refund_to, promise_create}; /// use near_sdk::{AccountId, Gas, NearToken}; /// use std::str::FromStr; /// /// let promise = promise_create( /// "account.near".parse().unwrap(), /// "method", /// [], /// NearToken::from_millinear(1), /// Gas::from_tgas(1), /// ); /// promise_set_refund_to(promise, &"refund.near".parse().unwrap()); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn promise_set_refund_to(promise_index: PromiseIndex, account_id: &AccountId) { let account_id: &str = account_id.as_ref(); unsafe { sys::promise_set_refund_to(promise_index.0, account_id.len() as _, account_id.as_ptr() as _) } } /// Appends `DeterministicStateInit` action to the batch of actions for the given promise /// pointed by `promise_index`. /// Uses low-level [`crate::sys::promise_batch_action_state_init`] /// /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_state_init, promise_create}; /// use near_sdk::{AccountId, CryptoHash, Gas, NearToken}; /// use std::str::FromStr; /// /// let promise = promise_create( /// "account.near".parse().unwrap(), /// "method", /// [], /// NearToken::from_millinear(1), /// Gas::from_tgas(1), /// ); /// promise_batch_action_state_init(promise, [0; 32], NearToken::from_millinear(1)); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn promise_batch_action_state_init( promise_index: PromiseIndex, code: CryptoHash, amount: NearToken, ) -> ActionIndex { unsafe { sys::promise_batch_action_state_init( promise_index.0, code.len() as _, code.as_ptr() as _, &amount.as_yoctonear() as *const u128 as _, ) } } /// Appends `DeterministicStateInit` action to the batch of actions for the given promise /// pointed by `promise_index`. /// Uses low-level [`crate::sys::promise_batch_action_state_init_by_account_id`] /// /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_state_init_by_account_id, promise_create}; /// use near_sdk::{AccountId, Gas, NearToken}; /// /// let account_id: AccountId = "account.near".parse().unwrap(); /// let promise = promise_create( /// account_id.clone(), /// "method", /// [], /// NearToken::from_millinear(1), /// Gas::from_tgas(1), /// ); /// promise_batch_action_state_init_by_account_id(promise, &account_id, NearToken::from_millinear(1)); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn promise_batch_action_state_init_by_account_id( promise_index: PromiseIndex, account_id: impl AsRef, amount: NearToken, ) -> ActionIndex { let account_id: &str = account_id.as_ref().as_str(); unsafe { sys::promise_batch_action_state_init_by_account_id( promise_index.0, account_id.len() as _, account_id.as_ptr() as _, &amount.as_yoctonear() as *const u128 as _, ) } } /// Appends a data entry to an existing `DeterministicStateInit` action. /// Uses low-level [`crate::sys::set_state_init_data_entry`] /// /// # Examples /// ``` /// use near_sdk::env::{set_state_init_data_entry, promise_batch_action_state_init_by_account_id, promise_create}; /// use near_sdk::{AccountId, Gas, NearToken}; /// use std::str::FromStr; /// /// let account_id: AccountId = "account.near".parse().unwrap(); /// let promise = promise_create( /// account_id.clone(), /// "method", /// [], /// NearToken::from_millinear(1), /// Gas::from_tgas(1), /// ); /// let action_index = promise_batch_action_state_init_by_account_id(promise, &account_id, NearToken::from_millinear(1)); /// set_state_init_data_entry(promise, action_index, b"key", b"value"); /// ``` #[cfg(feature = "deterministic-account-ids")] pub fn set_state_init_data_entry( promise_index: PromiseIndex, action_index: ActionIndex, key: &[u8], value: &[u8], ) { unsafe { sys::set_state_init_data_entry( promise_index.0, action_index, key.len() as _, key.as_ptr() as _, value.len() as _, value.as_ptr() as _, ) } } /// Attach a create account promise action to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_create_account, promise_batch_create}; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("new_account.near").unwrap() /// ); /// /// promise_batch_action_create_account(promise); /// ``` /// /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_create_account`] /// See example of usage [here](https://github.com/near/near-sdk-rs/blob/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_batch_action_create_account(promise_index: PromiseIndex) { unsafe { sys::promise_batch_action_create_account(promise_index.0) } } /// Attach a deploy contract promise action to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_deploy_contract, promise_batch_create}; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("contract.near").unwrap() /// ); /// /// let code = [0; 1487]; /// promise_batch_action_deploy_contract(promise, &code); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_deploy_contract`] /// See example of usage [here](https://github.com/near/near-sdk-rs/blob/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_batch_action_deploy_contract(promise_index: PromiseIndex, code: &[u8]) { unsafe { sys::promise_batch_action_deploy_contract( promise_index.0, code.len() as _, code.as_ptr() as _, ) } } /// Attach a function call promise action to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_function_call, promise_batch_create}; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("counter.near").unwrap() /// ); /// /// promise_batch_action_function_call( /// promise, /// "increase", /// serde_json::json!({ "value": 5 }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30) /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_function_call`] pub fn promise_batch_action_function_call( promise_index: PromiseIndex, function_name: &str, arguments: &[u8], amount: NearToken, gas: Gas, ) { unsafe { sys::promise_batch_action_function_call( promise_index.0, function_name.len() as _, function_name.as_ptr() as _, arguments.len() as _, arguments.as_ptr() as _, &amount.as_yoctonear() as *const u128 as _, gas.as_gas(), ) } } /// Attach a function call with specific gas weight promise action to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_function_call_weight, promise_batch_create}; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas, GasWeight}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("counter.near").unwrap() /// ); /// /// promise_batch_action_function_call_weight( /// promise, /// "increase", /// serde_json::json!({ "value": 5 }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30), /// GasWeight(1) /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_function_call_weight`] pub fn promise_batch_action_function_call_weight( promise_index: PromiseIndex, function_name: &str, arguments: &[u8], amount: NearToken, gas: Gas, weight: GasWeight, ) { unsafe { sys::promise_batch_action_function_call_weight( promise_index.0, function_name.len() as _, function_name.as_ptr() as _, arguments.len() as _, arguments.as_ptr() as _, &amount.as_yoctonear() as *const u128 as _, gas.as_gas(), weight.0, ) } } /// Attach a transfer promise action to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_transfer, promise_batch_create}; /// use near_sdk::{NearToken, AccountId}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// promise_batch_action_transfer( /// promise, /// NearToken::from_near(1), /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_transfer`] /// See example of usage [here](https://github.com/near/near-sdk-rs/blob/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_batch_action_transfer(promise_index: PromiseIndex, amount: NearToken) { unsafe { sys::promise_batch_action_transfer( promise_index.0, &amount.as_yoctonear() as *const u128 as _, ) } } /// Attach a stake promise action to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_stake, promise_batch_create}; /// use near_sdk::{NearToken, PublicKey, AccountId}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// let pk: PublicKey = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj".parse().unwrap(); /// promise_batch_action_stake( /// promise, /// NearToken::from_near(1), /// &pk /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_stake`] pub fn promise_batch_action_stake( promise_index: PromiseIndex, amount: NearToken, public_key: &PublicKey, ) { unsafe { sys::promise_batch_action_stake( promise_index.0, &amount.as_yoctonear() as *const u128 as _, public_key.as_bytes().len() as _, public_key.as_bytes().as_ptr() as _, ) } } /// Attach promise action that adds a full access key to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_add_key_with_full_access, promise_batch_create}; /// use near_sdk::{PublicKey, AccountId}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// let pk: PublicKey = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj".parse().unwrap(); /// let nonce = 55; /// promise_batch_action_add_key_with_full_access( /// promise, /// &pk, /// nonce /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_add_key_with_full_access`] /// See example of usage [here](https://github.com/near/near-sdk-rs/blob/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_batch_action_add_key_with_full_access( promise_index: PromiseIndex, public_key: &PublicKey, nonce: u64, ) { unsafe { sys::promise_batch_action_add_key_with_full_access( promise_index.0, public_key.as_bytes().len() as _, public_key.as_bytes().as_ptr() as _, nonce, ) } } /// This is a short lived function while we migrate between the Balance and the allowance type /// /// More info about batching [here](crate::env::promise_batch_create) pub(crate) fn migrate_to_allowance(allowance: NearToken) -> Allowance { Allowance::limited(allowance).unwrap_or(Allowance::Unlimited) } /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_add_key_with_function_call, promise_batch_create}; /// use near_sdk::{PublicKey, AccountId, NearToken}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// let pk: PublicKey = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj".parse().unwrap(); /// let nonce = 55; /// promise_batch_action_add_key_with_function_call( /// promise, /// &pk, /// nonce, /// NearToken::from_near(1), /// &AccountId::from_str("counter.near").unwrap(), /// "increase,decrease" /// ); /// ``` #[deprecated(since = "5.0.0", note = "Use add_access_key_allowance instead")] pub fn promise_batch_action_add_key_with_function_call( promise_index: PromiseIndex, public_key: &PublicKey, nonce: u64, allowance: NearToken, receiver_id: &AccountId, function_names: &str, ) { let allowance = migrate_to_allowance(allowance); promise_batch_action_add_key_allowance_with_function_call( promise_index, public_key, nonce, allowance, receiver_id, function_names, ) } /// Attach promise action that adds a key with function call with specifi allowance to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// Unlimited allowance /// ``` /// use near_sdk::env::{promise_batch_action_add_key_allowance_with_function_call, promise_batch_create}; /// use near_sdk::{PublicKey, AccountId, Allowance}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// let pk: PublicKey = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj".parse().unwrap(); /// let nonce = 55; /// promise_batch_action_add_key_allowance_with_function_call( /// promise, /// &pk, /// nonce, /// Allowance::unlimited(), /// &AccountId::from_str("counter.near").unwrap(), /// "increase,decrease" /// ); /// ``` /// /// Limited allowance (1 NEAR) /// ``` /// use near_sdk::env::{promise_batch_action_add_key_allowance_with_function_call, promise_batch_create}; /// use near_sdk::{PublicKey, AccountId, Allowance, NearToken}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// let pk: PublicKey = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj".parse().unwrap(); /// let nonce = 55; /// promise_batch_action_add_key_allowance_with_function_call( /// promise, /// &pk, /// nonce, /// Allowance::limited(NearToken::from_near(1)).unwrap(), /// &AccountId::from_str("counter.near").unwrap(), /// "increase,decrease" /// ); /// ``` pub fn promise_batch_action_add_key_allowance_with_function_call( promise_index: PromiseIndex, public_key: &PublicKey, nonce: u64, allowance: Allowance, receiver_id: &AccountId, function_names: &str, ) { let receiver_id: &str = receiver_id.as_ref(); let allowance = match allowance { Allowance::Limited(x) => x.get(), Allowance::Unlimited => 0, }; unsafe { sys::promise_batch_action_add_key_with_function_call( promise_index.0, public_key.as_bytes().len() as _, public_key.as_bytes().as_ptr() as _, nonce, &allowance as *const u128 as _, receiver_id.len() as _, receiver_id.as_ptr() as _, function_names.len() as _, function_names.as_ptr() as _, ) } } /// Attach promise action that deletes the key to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_delete_key, promise_batch_create}; /// use near_sdk::{PublicKey, AccountId}; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// let pk: PublicKey = "secp256k1:qMoRgcoXai4mBPsdbHi1wfyxF9TdbPCF4qSDQTRP3TfescSRoUdSx6nmeQoN3aiwGzwMyGXAb1gUjBTv5AY8DXj".parse().unwrap(); /// promise_batch_action_delete_key( /// promise, /// &pk /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_delete_key`] pub fn promise_batch_action_delete_key(promise_index: PromiseIndex, public_key: &PublicKey) { unsafe { sys::promise_batch_action_delete_key( promise_index.0, public_key.as_bytes().len() as _, public_key.as_bytes().as_ptr() as _, ) } } /// Attach promise action that deletes the account to the NEAR promise index with the provided promise index. /// /// More info about batching [here](crate::env::promise_batch_create) /// # Examples /// ``` /// use near_sdk::env::{promise_batch_action_delete_account, promise_batch_create}; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// let promise = promise_batch_create( /// &AccountId::from_str("receiver.near").unwrap() /// ); /// /// promise_batch_action_delete_account( /// promise, /// &AccountId::from_str("beneficiary.near").unwrap() /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_batch_action_delete_account`] pub fn promise_batch_action_delete_account( promise_index: PromiseIndex, beneficiary_id: &AccountId, ) { let beneficiary_id: &str = beneficiary_id.as_ref(); unsafe { sys::promise_batch_action_delete_account( promise_index.0, beneficiary_id.len() as _, beneficiary_id.as_ptr() as _, ) } } #[cfg(feature = "global-contracts")] /// Deploys a global contract using the provided contract code. /// /// # Arguments /// * `promise_index` - Promise batch index /// * `code` - Contract bytecode to deploy as a global contract /// /// # Examples /// ```no_run /// use near_sdk::{env, PromiseIndex}; /// /// let promise = env::promise_batch_create(&"alice.near".parse().unwrap()); /// let code = vec![0u8; 100]; // Contract bytecode /// env::promise_batch_action_deploy_global_contract(promise, &code); /// ``` pub fn promise_batch_action_deploy_global_contract(promise_index: PromiseIndex, code: &[u8]) { unsafe { sys::promise_batch_action_deploy_global_contract( promise_index.0, code.len() as _, code.as_ptr() as _, ) } } #[cfg(feature = "global-contracts")] /// Deploys a global contract by referencing another account's deployed code. /// /// # Arguments /// * `promise_index` - Promise batch index /// * `code` - Contract bytecode to deploy as a global contract /// /// # Examples /// ```no_run /// use near_sdk::{env, PromiseIndex}; /// /// let promise = env::promise_batch_create(&"alice.near".parse().unwrap()); /// let code = vec![0u8; 100]; // Contract bytecode /// env::promise_batch_action_deploy_global_contract_by_account_id(promise, &code); /// ``` pub fn promise_batch_action_deploy_global_contract_by_account_id( promise_index: PromiseIndex, code: &[u8], ) { unsafe { sys::promise_batch_action_deploy_global_contract_by_account_id( promise_index.0, code.len() as _, code.as_ptr() as _, ) } } #[cfg(feature = "global-contracts")] /// Uses an existing global contract by code hash. /// /// # Arguments /// * `promise_index` - Promise batch index /// * `code_hash` - Hash of the global contract code to use /// /// # Examples /// ```no_run /// use near_sdk::{env, PromiseIndex}; /// /// let promise = env::promise_batch_create(&"alice.near".parse().unwrap()); /// let code_hash = [0u8; 32]; // 32-byte hash (CryptoHash) /// env::promise_batch_action_use_global_contract(promise, &code_hash); /// ``` pub fn promise_batch_action_use_global_contract( promise_index: PromiseIndex, code_hash: &CryptoHash, ) { unsafe { sys::promise_batch_action_use_global_contract( promise_index.0, code_hash.len() as _, code_hash.as_ptr() as _, ) } } #[cfg(feature = "global-contracts")] /// Uses an existing global contract by referencing the account that deployed it. /// /// # Arguments /// * `promise_index` - Promise batch index /// * `account_id` - Account ID that deployed the global contract /// /// # Examples /// ```no_run /// use near_sdk::{env, PromiseIndex, AccountId}; /// use std::str::FromStr; /// /// let promise = env::promise_batch_create(&"alice.near".parse().unwrap()); /// env::promise_batch_action_use_global_contract_by_account_id( /// promise, /// &AccountId::from_str("deployer.near").unwrap() /// ); /// ``` pub fn promise_batch_action_use_global_contract_by_account_id( promise_index: PromiseIndex, account_id: impl AsRef, ) { let account_id: &str = account_id.as_ref().as_str(); unsafe { sys::promise_batch_action_use_global_contract_by_account_id( promise_index.0, account_id.len() as _, account_id.as_ptr() as _, ) } } /// If the current function is invoked by a callback we can access the execution results of the /// promises that caused the callback. This function returns the number of complete and /// incomplete callbacks. /// /// # Examples /// ``` /// use near_sdk::env::promise_results_count; /// /// assert_eq!(promise_results_count(), 0); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_results_count`] /// /// See example of usage [here](https://github.com/near/near-sdk-rs/blob/master/examples/cross-contract-calls/low-level/src/lib.rs) pub fn promise_results_count() -> u64 { maybe_cached!(u64: { unsafe { sys::promise_results_count() } }) } /// If the current function is invoked by a callback we can access the execution results of the /// promises that caused the callback. /// /// # Examples /// ```no_run /// use near_sdk::env::{promise_result, promise_results_count, log_str}; /// use near_sdk::PromiseResult; /// /// assert!(promise_results_count() > 0); /// /// // The promise_index will be in the range [0, n) /// // where n is the number of promises triggering this callback, /// // retrieved from promise_results_count() /// let promise_index = 0; /// let result = promise_result(promise_index); /// /// match result { /// PromiseResult::Successful(data) => { /// log_str(format!("Result as Vec: {:?}", data).as_str()); /// } /// PromiseResult::Failed => { /// log_str("Promise failed!"); /// } /// }; /// ``` /// /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_result`] /// /// Example usages: /// - [near-contract-standards/src/fungible_token](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/near-contract-standards/src/fungible_token/core_impl.rs#L178) /// - [near-contract-standards/src/non_fungible_token](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/near-contract-standards/src/non_fungible_token/core/core_impl.rs#L433) /// - [examples/factory-contract/low-level](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/examples/factory-contract/low-level/src/lib.rs#L61) /// - [examples/cross-contract-calls/low-level](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/examples/cross-contract-calls/low-level/src/lib.rs#L46) #[deprecated = "use `promise_result_checked` to prevent out-of-gas errors"] pub fn promise_result(result_idx: u64) -> PromiseResult { match promise_result_checked(result_idx, usize::MAX) { Ok(data) => PromiseResult::Successful(data), Err(err) => match err { PromiseError::Failed => PromiseResult::Failed, // vector can't be longer than usize::MAX PromiseError::TooLong(_len) => unreachable!(), }, } } /// Same as [`promise_result`] but bounded: if the result is longer than /// `max_len`, then `Err(PromiseResultError::TooLong(len))` is returned. /// /// This should be preferred when reading promise results from unknown /// contracts to prevent out-of-gas errors. /// /// # Examples /// ```no_run /// use near_sdk::env::{promise_result_checked, promise_results_count, log_str, panic_str}; /// use near_sdk::PromiseResult; /// /// assert!(promise_results_count() > 0); /// /// // The promise_index will be in the range [0, n) /// // where n is the number of promises triggering this callback, /// // retrieved from promise_results_count() /// let promise_index = 0; /// /// let data = promise_result_checked(promise_index, 100) /// .unwrap_or_else(|_| panic_str("Promise failed or returned result is too long!")); /// log_str(format!("Result as Vec: {:?}", data).as_str()); /// assert!(data.len() <= 100); /// ``` pub fn promise_result_checked(result_idx: u64, max_len: usize) -> Result, PromiseError> { promise_result_internal(result_idx)?; expect_register(read_register_bounded(ATOMIC_OP_REGISTER, max_len)) .map_err(PromiseError::TooLong) } pub(crate) fn promise_result_internal(result_idx: u64) -> Result<(), PromiseError> { match unsafe { sys::promise_result(result_idx, ATOMIC_OP_REGISTER) } { 1 => Ok(()), 2 => Err(PromiseError::Failed), _ => abort(), } } /// Consider the execution result of promise under `promise_idx` as execution result of this /// function. /// /// # Examples /// ``` /// use near_sdk::env::{promise_create, promise_return}; /// use near_sdk::serde_json; /// use near_sdk::{AccountId, NearToken, Gas}; /// use std::str::FromStr; /// /// let promise = promise_create( /// AccountId::from_str("counter.near").unwrap(), /// "increment", /// serde_json::json!({ /// "value": 5 /// }).to_string().into_bytes().as_slice(), /// NearToken::from_yoctonear(0), /// Gas::from_tgas(30) /// ); /// /// promise_return(promise); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_return`] /// /// Example usages: [one](https://github.com/near/near-sdk-rs/tree/master/examples/cross-contract-calls/low-level/src/lib.rs), [two](https://github.com/near/near-sdk-rs/tree/master/examples/factory-contract/low-level/src/lib.rs) pub fn promise_return(promise_idx: PromiseIndex) { unsafe { sys::promise_return(promise_idx.0) } } /// Creates a promise that will execute a method on the current account with given arguments. /// Writes a resumption token (data id) to the specified register. The callback method will execute /// after promise_yield_resume is called with the data id OR enough blocks have passed. The timeout /// length is specified as a protocol-level parameter yield_timeout_length_in_blocks = 200. /// /// The callback method will execute with a single promise input. Input will either be a payload /// provided by the user when calling promise_yield_resume, or a PromiseError in case of timeout. /// /// Resumption tokens are specific to the local account; promise_yield_resume must be called from /// a method of the same contract. /// /// # Examples /// ```no_run /// use near_sdk::env::{promise_yield_create, promise_yield_resume, read_register}; /// use near_sdk::serde_json; /// use near_sdk::{Gas, GasWeight, CryptoHash}; /// /// let DATA_ID_REGISTER = 0; /// // Create yield promise /// let promise = promise_yield_create( /// "increment", /// // passed as arguments /// serde_json::json!({ /// "value": 5 /// }).to_string().into_bytes().as_slice(), /// Gas::from_tgas(10), /// GasWeight(0), /// DATA_ID_REGISTER /// ); /// /// // Retrieve `data_id` for further resume /// let data_id: CryptoHash = read_register(DATA_ID_REGISTER) /// .expect("read_register failed") /// .try_into() /// .expect("conversion to CryptoHash failed"); /// /// // Resume execution using previously retrieved `data_id` /// promise_yield_resume( /// &data_id, /// // passed as callback_result /// serde_json::json!({ /// "key": "value", /// "description": "some text" /// }).to_string().into_bytes().as_slice() /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_yield_create`] /// See example of usage [here](https://github.com/near/mpc/blob/79ec50759146221e7ad8bb04520f13333b75ca07/chain-signatures/contract/src/lib.rs#L689) and [here](https://github.com/near/near-sdk-rs/blob/master/examples/mpc-contract/src/lib.rs#L45) pub fn promise_yield_create( function_name: &str, arguments: impl AsRef<[u8]>, gas: Gas, weight: GasWeight, register_id: u64, ) -> PromiseIndex { let arguments = arguments.as_ref(); unsafe { PromiseIndex(sys::promise_yield_create( function_name.len() as _, function_name.as_ptr() as _, arguments.len() as _, arguments.as_ptr() as _, gas.as_gas(), weight.0, register_id as _, )) } } /// Helper function that creates a yield promise and returns both the promise index and the yield ID. /// /// This is a convenience wrapper around [`promise_yield_create`] that automatically reads the /// yield ID from the register and returns it as a [`crate::YieldId`]. pub fn promise_yield_create_id( function_name: &str, arguments: impl AsRef<[u8]>, gas: Gas, weight: GasWeight, ) -> (PromiseIndex, crate::YieldId) { let promise_index = promise_yield_create(function_name, arguments, gas, weight, ATOMIC_OP_REGISTER); // SAFETY: promise_yield_create writes a 32-byte yield ID to the register let yield_id = crate::YieldId(unsafe { read_register_fixed(ATOMIC_OP_REGISTER) }); (promise_index, yield_id) } /// Accepts a resumption token `data_id` created by promise_yield_create on the local account. /// `data` is a payload to be passed to the callback method as a promise input. Returns false if /// no promise yield with the specified `data_id` is found. Returns true otherwise, guaranteeing /// that the callback method will be executed with a user-provided payload. /// /// If promise_yield_resume is called multiple times with the same `data_id`, it is possible to get /// back multiple 'true' results. The payload from the first successful call is passed to the /// callback. /// /// # Examples /// ```no_run /// use near_sdk::env::{promise_yield_create, promise_yield_resume, read_register}; /// use near_sdk::serde_json; /// use near_sdk::{Gas, GasWeight, CryptoHash}; /// /// let DATA_ID_REGISTER = 0; /// // Create yield promise /// let promise = promise_yield_create( /// "increment", /// // passed as arguments /// serde_json::json!({ /// "value": 5 /// }).to_string().into_bytes().as_slice(), /// Gas::from_tgas(10), /// GasWeight(0), /// DATA_ID_REGISTER /// ); /// /// // Retrieve `data_id` for further resume /// let data_id: CryptoHash = read_register(DATA_ID_REGISTER) /// .expect("read_register failed") /// .try_into() /// .expect("conversion to CryptoHash failed"); /// /// // Resume execution using previously retrieved `data_id` /// promise_yield_resume( /// &data_id, /// // passed as callback_result /// serde_json::json!({ /// "key": "value", /// "description": "some text" /// }).to_string().into_bytes().as_slice() /// ); /// ``` /// More low-level info here: [`near_vm_runner::logic::VMLogic::promise_yield_resume`] /// See example of usage [here](https://github.com/near/mpc/blob/79ec50759146221e7ad8bb04520f13333b75ca07/chain-signatures/contract/src/lib.rs#L288) and [here](https://github.com/near/near-sdk-rs/blob/master/examples/mpc-contract/src/lib.rs#L84) pub fn promise_yield_resume(data_id: &CryptoHash, data: impl AsRef<[u8]>) -> bool { let data = data.as_ref(); unsafe { sys::promise_yield_resume( data_id.len() as _, data_id.as_ptr() as _, data.len() as _, data.as_ptr() as _, ) != 0 } } // ############### // # Validator API # // ############### /// For a given account return its current stake. If the account is not a validator, returns 0. /// /// # Examples /// ``` /// use near_sdk::env::validator_stake; /// use near_sdk::{AccountId, NearToken}; /// use std::str::FromStr; /// /// assert_eq!( /// validator_stake(&AccountId::from_str("bob.near").unwrap()), /// NearToken::from_yoctonear(0) /// ); /// ``` pub fn validator_stake(account_id: &AccountId) -> NearToken { let account_id: &str = account_id.as_ref(); let mut data = [0u8; size_of::()]; unsafe { sys::validator_stake( account_id.len() as _, account_id.as_ptr() as _, data.as_mut_ptr() as u64, ) }; NearToken::from_yoctonear(u128::from_le_bytes(data)) } /// Returns the total stake of validators in the current epoch. /// /// # Examples /// ``` /// use near_sdk::env::validator_total_stake; /// use near_sdk::NearToken; /// /// assert_eq!( /// validator_total_stake(), /// NearToken::from_yoctonear(0) /// ); /// ``` pub fn validator_total_stake() -> NearToken { let mut data = [0u8; size_of::()]; unsafe { sys::validator_total_stake(data.as_mut_ptr() as u64) }; NearToken::from_yoctonear(u128::from_le_bytes(data)) } // ##################### // # Miscellaneous API # // ##################### /// Sets the blob of data as the return value of the contract. /// /// # Examples /// ``` /// use near_sdk::env::value_return; /// /// value_return(b"String data"); /// ``` /// ``` /// use near_sdk::env::value_return; /// use near_sdk::serde_json; /// /// value_return( /// serde_json::json!({ /// "account": "test.near", /// "value": 5 /// }).to_string().into_bytes().as_slice() /// ); /// ``` /// Example of usage [here](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/examples/cross-contract-calls/low-level/src/lib.rs#L18) pub fn value_return(value: impl AsRef<[u8]>) { let value = value.as_ref(); unsafe { sys::value_return(value.len() as _, value.as_ptr() as _) } } /// Terminates the execution of the program with the UTF-8 encoded message. /// [`panic_str`] should be used as the bytes are required to be UTF-8 /// /// # Examples /// ```should_panic /// use near_sdk::env::panic; /// /// panic(b"Unexpected error"); /// ``` #[deprecated(since = "4.0.0", note = "Use env::panic_str to panic with a message.")] pub fn panic(message: &[u8]) -> ! { unsafe { sys::panic_utf8(message.len() as _, message.as_ptr() as _) } } /// Terminates the execution of the program with the UTF-8 encoded message. /// /// # Examples /// ```should_panic /// use near_sdk::env::panic_str; /// /// panic_str("Unexpected error"); /// ``` /// ```should_panic /// use near_sdk::env::panic_str; /// use near_sdk::AccountId; /// use std::str::FromStr; /// /// let account = AccountId::from_str("bob.near").unwrap(); /// panic_str(format!("Unexpected error happened for account {}", account).as_str()); /// ``` pub fn panic_str(message: &str) -> ! { unsafe { sys::panic_utf8(message.len() as _, message.as_ptr() as _) } } /// Aborts the current contract execution without a custom message. /// To include a message, use [`panic_str`]. /// /// # Examples /// ```should_panic /// use near_sdk::env::abort; /// /// abort(); /// ``` pub fn abort() -> ! { // Use wasm32 unreachable call to avoid including the `panic` external function in Wasm. #[cfg(target_arch = "wasm32")] //* This was stabilized recently (~ >1.51), so ignore warnings but don't enforce higher msrv #[allow(unused_unsafe)] unsafe { core::arch::wasm32::unreachable() } #[cfg(not(target_arch = "wasm32"))] unsafe { sys::panic() } } /// Logs the string message message. This message is stored on chain. /// /// # Examples /// ``` /// use near_sdk::env::log_str; /// /// log_str("Some text"); /// ``` /// ``` /// use near_sdk::env::log_str; /// /// let number = 5; /// log_str(format!("Number: {}", number).as_str()); /// ``` /// Example of usage [here](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/near-contract-standards/src/event.rs#L29) pub fn log_str(message: &str) { #[cfg(all(debug_assertions, not(target_arch = "wasm32")))] eprintln!("{message}"); unsafe { sys::log_utf8(message.len() as _, message.as_ptr() as _) } } /// Log the UTF-8 encodable message. /// /// # Examples /// ``` /// use near_sdk::env::log; /// /// log(b"Text"); /// ``` #[deprecated(since = "4.0.0", note = "Use env::log_str for logging messages.")] pub fn log(message: &[u8]) { #[cfg(all(debug_assertions, not(target_arch = "wasm32")))] eprintln!("{}", String::from_utf8_lossy(message)); unsafe { sys::log_utf8(message.len() as _, message.as_ptr() as _) } } // ############### // # Storage API # // ############### /// Writes key-value into storage. /// If another key-value existed in the storage with the same key it returns `true`, otherwise `false`. /// /// # Use cases /// Storage functions are typically used to upgrade/migrate a contract state, preventing errors like `Cannot deserialize the contract state` after rolling out the breaking changes to the network. /// For practical examples, see different implementations in [this repository](https://github.com/near-examples/update-migrate-rust). /// /// # Examples /// ``` /// use near_sdk::env::{storage_write, storage_read}; /// /// assert!(!storage_write(b"key", b"value")); /// assert!(storage_write(b"key", b"another_value")); /// assert_eq!(storage_read(b"key").unwrap(), b"another_value"); /// ``` /// Example of usage [here](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/near-contract-standards/src/upgrade/mod.rs#L63) pub fn storage_write(key: &[u8], value: &[u8]) -> bool { match unsafe { sys::storage_write( key.len() as _, key.as_ptr() as _, value.len() as _, value.as_ptr() as _, EVICTED_REGISTER, ) } { 0 => false, 1 => true, _ => abort(), } } /// Reads the value stored under the given key. /// /// # Use cases /// /// Storage functions are typically used to upgrade/migrate a contract state, preventing errors like `Cannot deserialize the contract state` after rolling out the breaking changes to the network. /// /// For practical examples, see different implementations in [`near-examples/update-migrate-rust` repo](https://github.com/near-examples/update-migrate-rust). /// /// # Examples /// ``` /// use near_sdk::env::{storage_write, storage_read}; /// /// assert!(storage_read(b"key").is_none()); /// storage_write(b"key", b"value"); /// assert_eq!(storage_read(b"key").unwrap(), b"value"); /// ``` /// /// Another example: /// - [near-contract-standards/src/upgrade](https://github.com/near/near-sdk-rs/blob/746e4280a7e25b2036bd4e2f2c186cd76e1a7cde/near-contract-standards/src/upgrade/mod.rs?plain=1#L77) pub fn storage_read(key: &[u8]) -> Option> { match unsafe { sys::storage_read(key.len() as _, key.as_ptr() as _, ATOMIC_OP_REGISTER) } { 0 => None, 1 => Some(expect_register(read_register(ATOMIC_OP_REGISTER))), _ => abort(), } } /// Removes the value stored under the given key. /// If key-value existed returns `true`, otherwise `false`. /// /// # Use cases /// Storage functions are typically used to upgrade/migrate a contract state, preventing errors like `Cannot deserialize the contract state` after rolling out the breaking changes to the network. /// For practical examples, see different implementations in [this repository](https://github.com/near-examples/update-migrate-rust). /// /// # Examples /// ``` /// use near_sdk::env::{storage_write, storage_remove}; /// /// assert_eq!(storage_remove(b"key"), false); /// storage_write(b"key", b"value"); /// assert_eq!(storage_remove(b"key"), true); /// ``` /// Example of usage [here](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/near-contract-standards/src/upgrade/mod.rs#L79) pub fn storage_remove(key: &[u8]) -> bool { match unsafe { sys::storage_remove(key.len() as _, key.as_ptr() as _, EVICTED_REGISTER) } { 0 => false, 1 => true, _ => abort(), } } /// Reads the most recent value that was evicted with `storage_write` or `storage_remove` command. /// /// # Use cases /// Storage functions are typically used to upgrade/migrate a contract state, preventing errors like `Cannot deserialize the contract state` after rolling out the breaking changes to the network. /// For practical examples, see different implementations in [this repository](https://github.com/near-examples/update-migrate-rust). /// /// # Examples /// ``` /// use near_sdk::env::{storage_write, storage_remove, storage_get_evicted}; /// /// assert_eq!(storage_get_evicted(), None); /// ``` pub fn storage_get_evicted() -> Option> { read_register(EVICTED_REGISTER) } /// Checks if there is a key-value in the storage. /// /// # Use cases /// Storage functions are typically used to upgrade/migrate a contract state, preventing errors like `Cannot deserialize the contract state` after rolling out the breaking changes to the network. /// For practical examples, see different implementations in [this repository](https://github.com/near-examples/update-migrate-rust). /// /// # Examples /// ``` /// use near_sdk::env::{storage_write, storage_has_key}; /// /// assert_eq!(storage_has_key(b"key"), false); /// storage_write(b"key", b"value"); /// assert_eq!(storage_has_key(b"key"), true); /// ``` pub fn storage_has_key(key: &[u8]) -> bool { match unsafe { sys::storage_has_key(key.len() as _, key.as_ptr() as _) } { 0 => false, 1 => true, _ => abort(), } } // ############################################ // # Saving and loading of the contract state # // ############################################ /// Load the state of the given object. pub fn state_read() -> Option { storage_read(STATE_KEY).map(|data| { T::try_from_slice(&data) .unwrap_or_else(|_| panic_str("Cannot deserialize the contract state.")) }) } /// Writes the specified state to storage. pub fn state_write(state: &T) { let data = match borsh::to_vec(state) { Ok(serialized) => serialized, Err(_) => panic_str("Cannot serialize the contract state."), }; storage_write(STATE_KEY, &data); } /// Returns `true` if the contract state exists and `false` otherwise. pub fn state_exists() -> bool { storage_has_key(STATE_KEY) } // ##################################### // # Parameters exposed by the runtime # // ##################################### /// Price per 1 byte of storage from mainnet genesis config. /// TODO: will be using the host function when it will be available. /// /// # Examples /// ``` /// use near_sdk::env::storage_byte_cost; /// use near_sdk::NearToken; /// /// assert_eq!(storage_byte_cost(), NearToken::from_yoctonear(10000000000000000000)); /// ``` /// Example of usage [here](https://github.com/near/near-sdk-rs/blob/189897180649bce47aefa4e5af03664ee525508d/near-contract-standards/src/fungible_token/storage_impl.rs#L105), [here](https://github.com/near/near-sdk-rs/blob/master/near-contract-standards/src/non_fungible_token/utils.rs) and [here](https://github.com/near/near-sdk-rs/blob/master/examples/fungible-token/tests/workspaces.rs) pub fn storage_byte_cost() -> NearToken { NearToken::from_yoctonear(10_000_000_000_000_000_000u128) } // ################## // # Helper methods # // ################## /// Returns `true` if the given account ID is valid and `false` otherwise. /// /// # Examples /// /// ``` /// use near_sdk::env::is_valid_account_id; /// /// assert_eq!(is_valid_account_id(b"test.near"), true); /// assert_eq!(is_valid_account_id(b"test!.%.near"), false); /// ``` pub fn is_valid_account_id(account_id: &[u8]) -> bool { if (account_id.len() as u64) < MIN_ACCOUNT_ID_LEN || (account_id.len() as u64) > MAX_ACCOUNT_ID_LEN { return false; } // NOTE: We don't want to use Regex here, because it requires extra time to compile it. // The valid account ID regex is /^(([a-z\d]+[-_])*[a-z\d]+\.)*([a-z\d]+[-_])*[a-z\d]+$/ // Instead the implementation is based on the previous character checks. // We can safely assume that last char was a separator. let mut last_char_is_separator = true; for c in account_id { let current_char_is_separator = match *c { b'a'..=b'z' | b'0'..=b'9' => false, b'-' | b'_' | b'.' => true, _ => return false, }; if current_char_is_separator && last_char_is_separator { return false; } last_char_is_separator = current_char_is_separator; } // The account can't end as separator. !last_char_is_separator } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_valid_account_id_strings() { // Valid for account_id in &[ "aa", "a-a", "a-aa", "100", "0o", "com", "near", "bowen", "b-o_w_e-n", "b.owen", "bro.wen", "a.ha", "a.b-a.ra", "system", "over.9000", "google.com", "illia.cheapaccounts.near", "0o0ooo00oo00o", "alex-skidanov", "10-4.8-2", "b-o_w_e-n", "no_lols", "0123456789012345678901234567890123456789012345678901234567890123", // Valid, but can't be created "near.a", "a.a", ] { assert!( is_valid_account_id(account_id.as_ref()), "Valid account id {:?} marked invalid", account_id ); } // Invalid for account_id in &[ "", "a", "A", "Abc", "-near", "near-", "-near-", "near.", ".near", "near@", "@near", "неар", "@@@@@", "0__0", "0_-_0", "0_-_0", "..", "a..near", "nEar", "_bowen", "hello world", "abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz.abcdefghijklmnopqrstuvwxyz", "01234567890123456789012345678901234567890123456789012345678901234", // `@` separators are banned now "some-complex-address@gmail.com", "sub.buy_d1gitz@atata@b0-rg.c_0_m", ] { assert!( !is_valid_account_id(account_id.as_ref()), "Invalid account id {:?} marked valid", account_id ); } } #[test] fn test_is_valid_account_id_binary() { assert!(!is_valid_account_id(&[])); assert!(!is_valid_account_id(&[0])); assert!(!is_valid_account_id(&[0, 1])); assert!(!is_valid_account_id(&[0, 1, 2])); assert!(is_valid_account_id(b"near")); } #[cfg(not(target_arch = "wasm32"))] #[test] fn hash_smoke_tests() { assert_eq!( &super::sha256_array(b"some value"), hex::decode("ab3d07f3169ccbd0ed6c4b45de21519f9f938c72d24124998aab949ce83bb51b") .unwrap() .as_slice() ); assert_eq!( &super::keccak256_array(b"some value"), hex::decode("f928dfb5fc72b3bbfb9a5ccb0ee9843b27b4ac1ebc25a6f6f783e23ebd47ef1f") .unwrap() .as_slice() ); assert_eq!( &super::keccak512_array(b"some value"), hex::decode("3e38d140a85123374ee63ec208973aa39b87349d17ccac948a2493e18b18b5913220cd174b4f511aa97977009e16be485fc94f5e2743cb9bb0579d35ab410583") .unwrap() .as_slice() ); assert_eq!( &super::ripemd160_array(b"some value"), hex::decode("09f025fed704e1ecac8f88b2bda3e56876da03ac").unwrap().as_slice() ); } #[cfg(not(target_arch = "wasm32"))] #[test] fn random_seed_smoke_test() { crate::testing_env!(crate::test_utils::VMContextBuilder::new() .random_seed([8; 32]) .build()); assert_eq!(super::random_seed(), [8; 32]); } #[cfg(not(target_arch = "wasm32"))] #[cfg(feature = "unstable")] #[test] fn test_ecrecover() { use crate::test_utils::test_env; use hex::FromHex; use serde::de::Error; use serde::{Deserialize, Deserializer}; use serde_json::from_slice; use std::fmt::Display; #[derive(Deserialize)] struct EcrecoverTest { #[serde(with = "hex::serde")] m: [u8; 32], v: u8, #[serde(with = "hex::serde")] sig: [u8; 64], mc: bool, #[serde(deserialize_with = "deserialize_option_hex")] res: Option<[u8; 64]>, } fn deserialize_option_hex<'de, D, T>(deserializer: D) -> Result, D::Error> where D: Deserializer<'de>, T: FromHex, ::Error: Display, { Deserialize::deserialize(deserializer) .map(|v: Option<&str>| v.map(FromHex::from_hex).transpose().map_err(Error::custom)) .and_then(|v| v) } test_env::setup_free(); for EcrecoverTest { m, v, sig, mc, res } in from_slice::<'_, Vec<_>>(include_bytes!("../../tests/ecrecover-tests.json")).unwrap() { assert_eq!(super::ecrecover(&m, &sig, v, mc), res); } } #[cfg(not(target_arch = "wasm32"))] #[test] fn signer_public_key() { let key: PublicKey = "ed25519:6E8sCci9badyRkXb3JoRpBj5p8C6Tw41ELDZoiihKEtp".parse().unwrap(); crate::testing_env!(crate::test_utils::VMContextBuilder::new() .signer_account_pk(key.clone()) .build()); assert_eq!(super::signer_account_pk(), key); } #[test] fn ed25519_verify() { const SIGNATURE: [u8; 64] = [ 145, 193, 203, 18, 114, 227, 14, 117, 33, 213, 121, 66, 130, 14, 25, 4, 36, 120, 46, 142, 226, 215, 7, 66, 122, 112, 97, 30, 249, 135, 61, 165, 221, 249, 252, 23, 105, 40, 56, 70, 31, 152, 236, 141, 154, 122, 207, 20, 75, 118, 79, 90, 168, 6, 221, 122, 213, 29, 126, 196, 216, 104, 191, 6, ]; const BAD_SIGNATURE: [u8; 64] = [1; 64]; // create a forged signature with the `s` scalar not properly reduced // https://docs.rs/ed25519/latest/src/ed25519/lib.rs.html#302 const FORGED_SIGNATURE: [u8; 64] = { let mut sig = SIGNATURE; sig[63] = 0b1110_0001; sig }; const PUBLIC_KEY: [u8; 32] = [ 32, 122, 6, 120, 146, 130, 30, 37, 215, 112, 241, 251, 160, 196, 124, 17, 255, 75, 129, 62, 84, 22, 46, 206, 158, 184, 57, 224, 118, 35, 26, 182, ]; // create a forged public key to force a PointDecompressionError // https://docs.rs/ed25519-dalek/latest/src/ed25519_dalek/public.rs.html#142 const FORGED_PUBLIC_KEY: [u8; 32] = { let mut key = PUBLIC_KEY; key[31] = 0b1110_0001; key }; // 32 bytes message const MESSAGE: [u8; 32] = [ 107, 97, 106, 100, 108, 102, 107, 106, 97, 108, 107, 102, 106, 97, 107, 108, 102, 106, 100, 107, 108, 97, 100, 106, 102, 107, 108, 106, 97, 100, 115, 107, ]; assert!(super::ed25519_verify(&SIGNATURE, MESSAGE, &PUBLIC_KEY)); assert!(!super::ed25519_verify(&BAD_SIGNATURE, MESSAGE, &FORGED_PUBLIC_KEY)); assert!(!super::ed25519_verify(&SIGNATURE, MESSAGE, &FORGED_PUBLIC_KEY)); assert!(!super::ed25519_verify(&FORGED_SIGNATURE, MESSAGE, &PUBLIC_KEY)); } #[test] pub fn alt_bn128_g1_multiexp() { // Originated from https://github.com/near/nearcore/blob/8cd095ffc98a6507ed2d2a8982a6a3e42ebc1b62/runtime/near-test-contracts/estimator-contract/src/lib.rs#L557-L720 let buffer = [ 16, 238, 91, 161, 241, 22, 172, 158, 138, 252, 202, 212, 136, 37, 110, 231, 118, 220, 8, 45, 14, 153, 125, 217, 227, 87, 238, 238, 31, 138, 226, 8, 238, 185, 12, 155, 93, 126, 144, 248, 200, 177, 46, 245, 40, 162, 169, 80, 150, 211, 157, 13, 10, 36, 44, 232, 173, 32, 32, 115, 123, 2, 9, 47, 190, 148, 181, 91, 69, 6, 83, 40, 65, 222, 251, 70, 81, 73, 60, 142, 130, 217, 176, 20, 69, 75, 40, 167, 41, 180, 244, 5, 142, 215, 135, 35, ]; assert_eq!( super::alt_bn128_g1_multiexp(buffer), vec![ 150, 94, 159, 52, 239, 226, 181, 150, 77, 86, 90, 186, 102, 219, 243, 204, 36, 128, 164, 209, 106, 6, 62, 124, 235, 104, 223, 195, 30, 204, 42, 20, 13, 158, 14, 197, 133, 73, 43, 171, 28, 68, 82, 116, 244, 164, 36, 251, 244, 8, 234, 40, 118, 55, 216, 187, 242, 39, 213, 160, 192, 184, 28, 23 ] ); } #[test] pub fn alt_bn128_g1_sum() { // Originated from https://github.com/near/nearcore/blob/8cd095ffc98a6507ed2d2a8982a6a3e42ebc1b62/runtime/near-test-contracts/estimator-contract/src/lib.rs#L557-L720 let buffer = [ 0, 11, 49, 94, 29, 152, 111, 116, 138, 248, 2, 184, 8, 159, 80, 169, 45, 149, 48, 32, 49, 37, 6, 133, 105, 171, 194, 120, 44, 195, 17, 180, 35, 137, 154, 4, 192, 211, 244, 93, 200, 2, 44, 0, 64, 26, 108, 139, 147, 88, 235, 242, 23, 253, 52, 110, 236, 67, 99, 176, 2, 186, 198, 228, 25, ]; assert_eq!( super::alt_bn128_g1_sum(buffer), vec![ 11, 49, 94, 29, 152, 111, 116, 138, 248, 2, 184, 8, 159, 80, 169, 45, 149, 48, 32, 49, 37, 6, 133, 105, 171, 194, 120, 44, 195, 17, 180, 35, 137, 154, 4, 192, 211, 244, 93, 200, 2, 44, 0, 64, 26, 108, 139, 147, 88, 235, 242, 23, 253, 52, 110, 236, 67, 99, 176, 2, 186, 198, 228, 25 ] ); } #[test] pub fn alt_bn128_pairing_check() { // Taken from https://github.com/near/nearcore/blob/8cd095ffc98a6507ed2d2a8982a6a3e42ebc1b62/runtime/near-vm-runner/src/logic/tests/alt_bn128.rs#L239-L250 let valid_pair = [ 117, 10, 217, 99, 113, 78, 234, 67, 183, 90, 26, 58, 200, 86, 195, 123, 42, 184, 213, 88, 224, 248, 18, 200, 108, 6, 181, 6, 28, 17, 99, 7, 36, 134, 53, 115, 192, 180, 3, 113, 76, 227, 174, 147, 50, 174, 79, 74, 151, 195, 172, 10, 211, 210, 26, 92, 117, 246, 65, 237, 168, 104, 16, 4, 1, 26, 3, 219, 6, 13, 193, 115, 77, 230, 27, 13, 242, 214, 195, 9, 213, 99, 135, 12, 160, 202, 114, 135, 175, 42, 116, 172, 79, 234, 26, 41, 212, 111, 192, 129, 124, 112, 57, 107, 38, 244, 230, 222, 240, 36, 65, 238, 133, 188, 19, 43, 148, 59, 205, 40, 161, 179, 173, 228, 88, 169, 231, 29, 17, 67, 163, 51, 165, 187, 101, 44, 250, 24, 68, 101, 92, 128, 203, 190, 51, 85, 9, 43, 58, 136, 68, 180, 92, 110, 185, 168, 107, 129, 45, 30, 187, 22, 100, 17, 75, 93, 216, 125, 23, 212, 11, 186, 199, 204, 1, 140, 133, 11, 82, 44, 65, 222, 20, 26, 48, 26, 132, 220, 25, 213, 93, 25, 79, 176, 4, 149, 151, 243, 11, 131, 253, 233, 121, 38, 222, 15, 118, 117, 200, 214, 175, 233, 130, 181, 193, 167, 255, 153, 169, 240, 207, 235, 28, 31, 83, 74, 69, 179, 6, 150, 72, 67, 74, 166, 130, 83, 82, 115, 123, 111, 208, 221, 64, 43, 237, 213, 186, 235, 7, 56, 251, 179, 95, 233, 159, 23, 109, 173, 85, 103, 8, 165, 235, 226, 218, 79, 72, 120, 172, 251, 20, 83, 121, 201, 140, 98, 170, 246, 121, 218, 19, 115, 42, 135, 60, 239, 30, 32, 49, 170, 171, 204, 196, 197, 160, 158, 168, 47, 23, 110, 139, 123, 222, 222, 245, 98, 125, 208, 70, 39, 110, 186, 146, 254, 66, 185, 118, 3, 78, 32, 47, 179, 197, 93, 79, 240, 204, 78, 236, 133, 213, 173, 117, 94, 63, 154, 68, 89, 236, 138, 0, 247, 242, 212, 245, 33, 249, 0, 35, 246, 233, 0, 124, 86, 198, 162, 201, 54, 19, 26, 196, 75, 254, 71, 70, 238, 51, 2, 23, 185, 152, 139, 134, 65, 107, 129, 114, 244, 47, 251, 240, 80, 193, 23, ]; assert!(super::alt_bn128_pairing_check(valid_pair)); // Taken from https://github.com/near/nearcore/blob/8cd095ffc98a6507ed2d2a8982a6a3e42ebc1b62/runtime/near-vm-runner/src/logic/tests/alt_bn128.rs#L254-L265 let invalid_pair = [ 117, 10, 217, 99, 113, 78, 234, 67, 183, 90, 26, 58, 200, 86, 195, 123, 42, 184, 213, 88, 224, 248, 18, 200, 108, 6, 181, 6, 28, 17, 99, 7, 36, 134, 53, 115, 192, 180, 3, 113, 76, 227, 174, 147, 50, 174, 79, 74, 151, 195, 172, 10, 211, 210, 26, 92, 117, 246, 65, 237, 168, 104, 16, 4, 1, 26, 3, 219, 6, 13, 193, 115, 77, 230, 27, 13, 242, 214, 195, 9, 213, 99, 135, 12, 160, 202, 114, 135, 175, 42, 116, 172, 79, 234, 26, 41, 212, 111, 192, 129, 124, 112, 57, 107, 38, 244, 230, 222, 240, 36, 65, 238, 133, 188, 19, 43, 148, 59, 205, 40, 161, 179, 173, 228, 88, 169, 231, 29, 17, 67, 163, 51, 165, 187, 101, 44, 250, 24, 68, 101, 92, 128, 203, 190, 51, 85, 9, 43, 58, 136, 68, 180, 92, 110, 185, 168, 107, 129, 45, 30, 187, 22, 100, 17, 75, 93, 216, 125, 23, 212, 11, 186, 199, 204, 1, 140, 133, 11, 82, 44, 65, 222, 20, 26, 48, 26, 132, 220, 25, 213, 93, 25, 117, 10, 217, 99, 113, 78, 234, 67, 183, 90, 26, 58, 200, 86, 195, 123, 42, 184, 213, 88, 224, 248, 18, 200, 108, 6, 181, 6, 28, 17, 99, 7, 36, 134, 53, 115, 192, 180, 3, 113, 76, 227, 174, 147, 50, 174, 79, 74, 151, 195, 172, 10, 211, 210, 26, 92, 117, 246, 65, 237, 168, 104, 16, 4, 109, 173, 85, 103, 8, 165, 235, 226, 218, 79, 72, 120, 172, 251, 20, 83, 121, 201, 140, 98, 170, 246, 121, 218, 19, 115, 42, 135, 60, 239, 30, 32, 49, 170, 171, 204, 196, 197, 160, 158, 168, 47, 23, 110, 139, 123, 222, 222, 245, 98, 125, 208, 70, 39, 110, 186, 146, 254, 66, 185, 118, 3, 78, 32, 47, 179, 197, 93, 79, 240, 204, 78, 236, 133, 213, 173, 117, 94, 63, 154, 68, 89, 236, 138, 0, 247, 242, 212, 245, 33, 249, 0, 35, 246, 233, 0, 124, 86, 198, 162, 201, 54, 19, 26, 196, 75, 254, 71, 70, 238, 51, 2, 23, 185, 152, 139, 134, 65, 107, 129, 114, 244, 47, 251, 240, 80, 193, 23, ]; assert!(!super::alt_bn128_pairing_check(invalid_pair)); } #[test] fn bls12381_p1_sum_0_100() { let buffer: [u8; 0] = []; for _ in 0..100 { let result = super::bls12381_p1_sum(buffer); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_p1_sum"); } } #[test] fn bls12381_p1_sum_50_100() { let buffer: [[u8; 2 * 97]; 25] = [[ 0, 18, 25, 108, 90, 67, 214, 146, 36, 216, 113, 51, 137, 40, 95, 38, 185, 143, 134, 238, 145, 10, 179, 221, 102, 142, 65, 55, 56, 40, 32, 3, 204, 91, 115, 87, 175, 154, 122, 245, 75, 183, 19, 214, 34, 85, 232, 15, 86, 6, 186, 129, 2, 191, 190, 234, 68, 22, 183, 16, 199, 62, 140, 206, 48, 50, 195, 28, 98, 105, 196, 73, 6, 248, 172, 79, 120, 116, 206, 153, 251, 23, 85, 153, 146, 72, 101, 40, 150, 56, 132, 206, 66, 154, 153, 47, 238, 0, 0, 1, 16, 16, 152, 245, 195, 152, 147, 118, 87, 102, 175, 69, 18, 160, 199, 78, 27, 184, 155, 199, 230, 253, 241, 78, 62, 115, 55, 210, 87, 204, 15, 148, 101, 129, 121, 216, 51, 32, 185, 159, 49, 255, 148, 205, 43, 172, 3, 225, 169, 249, 244, 76, 162, 205, 171, 79, 67, 161, 163, 238, 52, 112, 253, 249, 11, 47, 194, 40, 235, 59, 112, 159, 205, 114, 240, 20, 131, 138, 200, 42, 109, 121, 122, 238, 254, 217, 160, 128, 75, 34, 237, 28, 232, 247, ]; 25]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); for _ in 0..100 { let result = super::bls12381_p1_sum(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_p1_sum"); } } #[test] fn bls12381_p2_sum_0_100() { let buffer: [u8; 0] = []; for _ in 0..100 { let result = super::bls12381_p2_sum(buffer); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_p2_sum"); } } #[test] fn bls12381_p2_sum_50_100() { let buffer: [[u8; 2 * 193]; 25] = [[ 0, 12, 199, 10, 88, 127, 70, 82, 3, 157, 129, 23, 182, 16, 56, 88, 173, 205, 151, 40, 246, 174, 190, 35, 5, 120, 56, 154, 98, 218, 0, 66, 183, 98, 59, 28, 4, 54, 115, 79, 70, 60, 253, 209, 135, 210, 9, 3, 36, 24, 192, 173, 166, 53, 27, 112, 102, 31, 5, 51, 101, 222, 174, 86, 145, 7, 152, 189, 42, 206, 110, 43, 246, 186, 65, 146, 209, 162, 41, 150, 127, 106, 246, 202, 28, 154, 138, 17, 235, 192, 162, 50, 52, 78, 224, 246, 214, 7, 155, 165, 13, 37, 17, 99, 27, 32, 182, 214, 243, 132, 30, 97, 110, 157, 17, 182, 142, 195, 54, 140, 214, 1, 41, 217, 212, 120, 122, 181, 108, 78, 145, 69, 163, 137, 39, 229, 28, 156, 214, 39, 29, 73, 61, 147, 136, 9, 245, 11, 215, 190, 237, 178, 51, 40, 129, 143, 159, 253, 175, 219, 109, 166, 164, 221, 128, 197, 169, 4, 138, 184, 177, 84, 223, 60, 173, 147, 140, 206, 222, 130, 159, 17, 86, 247, 105, 217, 225, 73, 121, 30, 142, 12, 217, 0, 9, 174, 177, 12, 55, 43, 94, 241, 1, 6, 117, 198, 164, 118, 47, 218, 51, 99, 100, 137, 194, 59, 88, 28, 117, 34, 5, 137, 175, 188, 12, 196, 98, 73, 249, 33, 238, 160, 45, 209, 183, 97, 224, 54, 255, 219, 174, 34, 25, 47, 165, 216, 115, 47, 249, 243, 142, 11, 28, 241, 46, 173, 253, 38, 8, 240, 199, 163, 154, 206, 215, 116, 104, 55, 131, 58, 226, 83, 187, 87, 239, 156, 13, 152, 164, 182, 158, 235, 41, 80, 144, 25, 23, 233, 157, 30, 23, 72, 130, 205, 211, 85, 30, 12, 230, 23, 136, 97, 255, 131, 225, 149, 254, 203, 207, 253, 83, 166, 123, 111, 16, 180, 67, 30, 66, 62, 40, 164, 128, 50, 127, 235, 231, 2, 118, 3, 111, 96, 187, 156, 153, 207, 118, 51, 2, 210, 37, 68, 118, 0, 212, 159, 147, 43, 157, 211, 202, 30, 105, 89, 105, 122, 166, 3, 231, 77, 134, 102, 104, 26, 45, 202, 129, 96, 195, 133, 118, 104, 174, 7, 68, 64, 54, 102, 25, 235, 137, 32, 37, 108, 78, 74, ]; 25]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_p2_sum(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_p2_sum"); } #[test] fn bls12381_g1_multiexp_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_g1_multiexp(buffer); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_g1_multiexp"); } #[test] fn bls12381_g1_multiexp_50_100() { let buffer: [[u8; 96 + 32]; 50] = [[ 23, 241, 211, 167, 49, 151, 215, 148, 38, 149, 99, 140, 79, 169, 172, 15, 195, 104, 140, 79, 151, 116, 185, 5, 161, 78, 58, 63, 23, 27, 172, 88, 108, 85, 232, 63, 249, 122, 26, 239, 251, 58, 240, 10, 219, 34, 198, 187, 8, 179, 244, 129, 227, 170, 160, 241, 160, 158, 48, 237, 116, 29, 138, 228, 252, 245, 224, 149, 213, 208, 10, 246, 0, 219, 24, 203, 44, 4, 179, 237, 208, 60, 199, 68, 162, 136, 138, 228, 12, 170, 35, 41, 70, 197, 231, 225, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, ]; 50]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_g1_multiexp(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_g1_multiexp"); } #[test] fn bls12381_g2_multiexp_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_g2_multiexp(buffer); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_g2_multiexp"); } #[test] fn bls12381_g2_multiexp_50_100() { let buffer: [[u8; 192 + 32]; 50] = [[ 19, 224, 43, 96, 82, 113, 159, 96, 125, 172, 211, 160, 136, 39, 79, 101, 89, 107, 208, 208, 153, 32, 182, 26, 181, 218, 97, 187, 220, 127, 80, 73, 51, 76, 241, 18, 19, 148, 93, 87, 229, 172, 125, 5, 93, 4, 43, 126, 2, 74, 162, 178, 240, 143, 10, 145, 38, 8, 5, 39, 45, 197, 16, 81, 198, 228, 122, 212, 250, 64, 59, 2, 180, 81, 11, 100, 122, 227, 209, 119, 11, 172, 3, 38, 168, 5, 187, 239, 212, 128, 86, 200, 193, 33, 189, 184, 6, 6, 196, 160, 46, 167, 52, 204, 50, 172, 210, 176, 43, 194, 139, 153, 203, 62, 40, 126, 133, 167, 99, 175, 38, 116, 146, 171, 87, 46, 153, 171, 63, 55, 13, 39, 92, 236, 29, 161, 170, 169, 7, 95, 240, 95, 121, 190, 12, 229, 213, 39, 114, 125, 110, 17, 140, 201, 205, 198, 218, 46, 53, 26, 173, 253, 155, 170, 140, 189, 211, 167, 109, 66, 154, 105, 81, 96, 209, 44, 146, 58, 201, 204, 59, 172, 162, 137, 225, 147, 84, 134, 8, 184, 40, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, ]; 50]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_g2_multiexp(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_g2_multiexp"); } #[test] fn bls12381_map_fp_to_g1_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_map_fp_to_g1(buffer); assert!(result.is_empty(), "Expected an empty result from bls12381_map_fp_to_g1"); } #[test] fn bls12381_map_fp_to_g1_50_100() { let buffer: [[u8; 48]; 50] = [[ 20, 64, 110, 91, 251, 146, 9, 37, 106, 56, 32, 135, 154, 41, 172, 47, 98, 214, 172, 168, 35, 36, 191, 58, 226, 170, 125, 60, 84, 121, 32, 67, 189, 140, 121, 31, 204, 219, 8, 12, 26, 82, 220, 104, 184, 182, 147, 80, ]; 50]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_map_fp_to_g1(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_map_fp_to_g1"); } #[test] fn bls12381_map_fp2_to_g2_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_map_fp2_to_g2(buffer); assert!(result.is_empty(), "Expected an empty result from bls12381_map_fp2_to_g2"); } #[test] fn bls12381_map_fp2_to_g2_10_100() { let buffer: [[u8; 96]; 10] = [[ 14, 136, 91, 179, 57, 150, 225, 47, 7, 218, 105, 7, 62, 44, 12, 200, 128, 188, 142, 255, 38, 210, 167, 36, 41, 158, 177, 45, 84, 244, 188, 242, 111, 71, 72, 187, 2, 14, 128, 167, 227, 121, 74, 123, 14, 71, 166, 65, 20, 64, 110, 91, 251, 146, 9, 37, 106, 56, 32, 135, 154, 41, 172, 47, 98, 214, 172, 168, 35, 36, 191, 58, 226, 170, 125, 60, 84, 121, 32, 67, 189, 140, 121, 31, 204, 219, 8, 12, 26, 82, 220, 104, 184, 182, 147, 80, ]; 10]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_map_fp2_to_g2(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_map_fp2_to_g2"); } #[test] fn bls12381_pairing_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_pairing_check(buffer); assert!(result, "Expected result to be true"); } #[test] fn bls12381_pairing_valid_check() { // Valid test vector (should return true) let valid_input = hex::decode("085fad8696122c8a421033164e6a71d9adb3882933beba2c14dcad9bfd4badb30b49306c59a7a7837b72e02993f5a4ad025871da31a9be44cd3a46365038ef6f3658fc65ff3064e348083b2de4d983c7436f486f6e9de272fa0db7dfa543656811f7dbc8c5b084e2daf685536a2d155d69c7683b811c840e4167a5c966bad4eebfdb757ef9caa63ffde16727fa5c15ac0b15a2802624e85d6987eb53a69714401adfd5ca5e6151a8e9c0790dfc4494ea77ad32b66e95da7f615ee2fe7b6594f00493deb2392b4159afc07b69000f9b097ecca94bf5a46cb13f95dabdd9a40a2e207c077059c821caa29a40930b4b757f11404dcfe5e92c69acdbf3667651d5adf6856956805693fb945d83c5cf158371536814442ff31d6ad1b834a4ab13ad9917f1d3a73197d7942695638c4fa9ac0fc3688c4f9774b905a14e3a3f171bac586c55e83ff97a1aeffb3af00adb22c6bb114d1d6855d545a8aa7d76c8cf2e21f267816aef1db507c96655b9d5caac42364e6f38ba0ecb751bad54dcd6b939c2ca0f968bd243908ff3e5fa1ab3f31e078197e58ace562bbe8b5a271d5fba50237da0c8fe65e7b5771cc0a86fd57f32347e15a26d1f5d56c472d019eea2539e58db00c49aa5d0a9663838903fddbe436b5b157e83b35d1a4e5f89f78127f35dacf005a2854c7f36818c137070d1342bba362b5d0c7daed605fcc739df577c33bd6ab6e07ab4a97beee81aa57c8d41f447440eeaf1f595b7b57457d7792b4bc14be74d0038f7ac3767a9c61fecaa02c3d07982c02995f22f66c05b8eb3b9facd5571").unwrap(); let result = super::bls12381_pairing_check(&valid_input); assert!(result, "Expected valid pairing check to return true"); } #[test] fn bls12381_pairing_5_100() { let buffer: [[u8; 288]; 5] = [[ 23, 241, 211, 167, 49, 151, 215, 148, 38, 149, 99, 140, 79, 169, 172, 15, 195, 104, 140, 79, 151, 116, 185, 5, 161, 78, 58, 63, 23, 27, 172, 88, 108, 85, 232, 63, 249, 122, 26, 239, 251, 58, 240, 10, 219, 34, 198, 187, 8, 179, 244, 129, 227, 170, 160, 241, 160, 158, 48, 237, 116, 29, 138, 228, 252, 245, 224, 149, 213, 208, 10, 246, 0, 219, 24, 203, 44, 4, 179, 237, 208, 60, 199, 68, 162, 136, 138, 228, 12, 170, 35, 41, 70, 197, 231, 225, 19, 224, 43, 96, 82, 113, 159, 96, 125, 172, 211, 160, 136, 39, 79, 101, 89, 107, 208, 208, 153, 32, 182, 26, 181, 218, 97, 187, 220, 127, 80, 73, 51, 76, 241, 18, 19, 148, 93, 87, 229, 172, 125, 5, 93, 4, 43, 126, 2, 74, 162, 178, 240, 143, 10, 145, 38, 8, 5, 39, 45, 197, 16, 81, 198, 228, 122, 212, 250, 64, 59, 2, 180, 81, 11, 100, 122, 227, 209, 119, 11, 172, 3, 38, 168, 5, 187, 239, 212, 128, 86, 200, 193, 33, 189, 184, 6, 6, 196, 160, 46, 167, 52, 204, 50, 172, 210, 176, 43, 194, 139, 153, 203, 62, 40, 126, 133, 167, 99, 175, 38, 116, 146, 171, 87, 46, 153, 171, 63, 55, 13, 39, 92, 236, 29, 161, 170, 169, 7, 95, 240, 95, 121, 190, 12, 229, 213, 39, 114, 125, 110, 17, 140, 201, 205, 198, 218, 46, 53, 26, 173, 253, 155, 170, 140, 189, 211, 167, 109, 66, 154, 105, 81, 96, 209, 44, 146, 58, 201, 204, 59, 172, 162, 137, 225, 147, 84, 134, 8, 184, 40, 1, ]; 5]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_pairing_check(&flat); assert!(!result, "Expected result to be false"); } #[test] fn bls12381_p1_decompress_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_p1_decompress(buffer); assert!(result.is_empty(), "Expected an empty result from bls12381_p1_decompress"); } #[test] fn bls12381_p1_decompress_50_100() { let buffer: [[u8; 48]; 50] = [[ 185, 110, 35, 139, 110, 142, 126, 177, 120, 97, 234, 41, 91, 204, 20, 203, 207, 103, 224, 112, 176, 18, 102, 59, 68, 107, 137, 231, 10, 71, 183, 63, 198, 228, 242, 206, 195, 124, 70, 91, 53, 182, 222, 158, 19, 104, 106, 15, ]; 50]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_p1_decompress(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_p1_decompress"); } #[test] fn bls12381_p2_decompress_0_100() { let buffer: [u8; 0] = []; let result = super::bls12381_p2_decompress(buffer); assert!(result.is_empty(), "Expected an empty result from bls12381_p2_decompress"); } #[test] fn bls12381_p2_decompress_50_100() { let buffer: [[u8; 96]; 50] = [[ 143, 150, 139, 210, 67, 144, 143, 243, 229, 250, 26, 179, 243, 30, 7, 129, 151, 229, 138, 206, 86, 43, 190, 139, 90, 39, 29, 95, 186, 80, 35, 125, 160, 200, 254, 101, 231, 181, 119, 28, 192, 168, 111, 213, 127, 50, 52, 126, 21, 162, 109, 31, 93, 86, 196, 114, 208, 25, 238, 162, 83, 158, 88, 219, 0, 196, 154, 165, 208, 169, 102, 56, 56, 144, 63, 221, 190, 67, 107, 91, 21, 126, 131, 179, 93, 26, 78, 95, 137, 247, 129, 39, 243, 93, 172, 240, ]; 50]; let flat: Vec = buffer.iter().flat_map(|x| x.iter()).copied().collect(); let result = super::bls12381_p2_decompress(&flat); assert!(!result.is_empty(), "Expected a non-empty result from bls12381_p2_decompress"); } #[test] #[cfg(feature = "global-contracts")] fn test_global_contract_functions() { // Test the global contract promise batch action functions // These tests verify the functions can be called without panicking let promise_index = super::promise_batch_create(&"alice.near".parse().unwrap()); let code = vec![0u8; 100]; // Mock contract bytecode let code_hash = [0u8; 32]; // Mock 32-byte hash (CryptoHash) let account_id = crate::AccountIdRef::new_or_panic("deployer.near"); // Test deploy_global_contract super::promise_batch_action_deploy_global_contract(promise_index, &code); // Test deploy_global_contract_by_account_id super::promise_batch_action_deploy_global_contract_by_account_id(promise_index, &code); // Test use_global_contract super::promise_batch_action_use_global_contract(promise_index, &code_hash); // Test use_global_contract_by_account_id super::promise_batch_action_use_global_contract_by_account_id(promise_index, account_id); } #[test] #[cfg(feature = "global-contracts")] fn test_global_contract_edge_cases() { // Test with minimal valid inputs let promise_index = super::promise_batch_create(&"alice.near".parse().unwrap()); // Test with single byte code (minimal size) super::promise_batch_action_deploy_global_contract(promise_index, &[0]); super::promise_batch_action_deploy_global_contract_by_account_id(promise_index, &[0]); // Test with 32-byte hash (standard size for CryptoHash) let valid_hash = [0u8; 32]; super::promise_batch_action_use_global_contract(promise_index, &valid_hash); } } ```
:::
## Optimizing Python Contracts Since Python smart contracts on NEAR are interpreted rather than compiled to WebAssembly directly, the optimization strategies differ from Rust contracts. The Python interpreter itself is already optimized, but you can still make your contract more efficient. ### Reducing Contract Size #### 1. Minimize Dependencies Only import the modules and functions you need: ```python # Less efficient - imports everything from near_sdk_py import * # More efficient - imports only what's needed from near_sdk_py import view, call, Context ``` #### 2. Use Efficient Data Structures Choose the right data structure for your needs: ```python # Less efficient for lookups user_list = [] # O(n) lookup time for user in user_list: if user["id"] == user_id: return user # More efficient for lookups user_dict = {} # O(1) lookup time if user_id in user_dict: return user_dict[user_id] ``` #### 3. Optimize Storage Usage The NEAR SDK collections are optimized for on-chain storage. For large data sets, use these instead of native Python collections: ```python # Native collection - loaded entirely into memory self.users = {} # Loaded entirely on each method call # SDK collection - lazily loaded from near_sdk_py.collections import UnorderedMap self.users = UnorderedMap("u") # Only loads what's needed ``` #### 4. Reduce String Operations String operations can be expensive. Minimize them when possible: ```python # Less efficient result = "" for i in range(100): result += str(i) # Creates many intermediate strings # More efficient parts = [] for i in range(100): parts.append(str(i)) result = "".join(parts) # Creates strings once ``` #### 5. Avoid Recursion Deep recursion can lead to stack overflow issues. Consider iterative approaches instead: ```python # Recursive - could cause issues with deep structures def process_tree(node): result = process_node(node) for child in node.children: result += process_tree(child) return result # Iterative - more efficient def process_tree(root): result = 0 queue = [root] while queue: node = queue.pop(0) result += process_node(node) queue.extend(node.children) return result ``` #### 6. Use Python's Built-in Functions Python's built-in functions are often optimized and more efficient: ```python # Less efficient sum_value = 0 for num in numbers: sum_value += num # More efficient sum_value = sum(numbers) ``` #### 7. Split Complex Logic If you have a very large contract, consider splitting it into multiple contracts with focused responsibilities. This can improve maintainability and reduce the cost of individual calls. #### 8. Profile and Optimize Hot Paths Focus optimization efforts on the most frequently called functions or those that handle large amounts of data: ```python @call def frequently_called_method(self, data): # Optimize this code carefully # Consider using more efficient algorithms and data structures pass ```
--- # Source: https://docs.near.org/smart-contracts/security/reentrancy.md --- id: reentrancy title: Reentrancy Attacks description: "Learn about reentrancy attacks in NEAR smart contracts and how to prevent them with proper security measures and coding practices." --- Between a cross-contract call and its callback **any method of your contract can be executed**. Not taking this into account is one of the main sources of exploits. It is so common that it has its own name: **reentrancy attacks**. Always make sure to keep your state in a consistent state after a method finishes executing. Assume that: - Any method could be executed between a method execution and its callback. - The same method could be executed again before the callback kicks in. --- ### Example Imagine that we develop a `deposit_and_stake` with the following **wrong logic**: (1) The user sends us money, (2) we add it to its balance, (3) we try to stake it in a validator, (4) if the staking fails, we remove the balance in the callback. Then, a user could schedule a call to withdraw between (2) and (4), and, if the staking failed, we would send money twice to the user. ![img](https://miro.medium.com/max/1400/1*VweWHQYGLBa70uceiWHLQA.png) *Between a cross-contract call and the callback anything could happen* Luckily for us the solution is rather simple. Instead of immediately adding the money to our user’s balance, we wait until the callback. There we check, and if the staking went well, then we add it to their balance. ![img](https://miro.medium.com/max/1400/1*o0YVDCp_7l-L3njJMGhU4w.png) *Correct way to handle deposits in a cross-contract call* --- # Source: https://docs.near.org/smart-contracts/anatomy/reproducible-builds.md --- id: reproducible-builds title: Reproducible Builds description: "Create identical builds across different developer environments." --- Reproducible builds let different people build the same program and get the exact same outputs as one another. They help users trust that deployed contracts are built correctly and correspond to the source code. To verify your contract, users can build it themselves and check that the binaries are identical. ## Problem If you build your contract on two different machines, most likely you will get two similar but not identical binaries. Your build artifact can be affected by the locale, timezone, build path, and billions of other factors in your build environment. Thus, the Rust community has a long history of addressing this issue. To achieve reproducible builds, NEAR utilizes several components such as [NEP-330-Source Metadata](https://github.com/near/NEPs/blob/master/neps/nep-0330.md), [cargo-near](https://github.com/near/cargo-near), [Docker](https://docker.com), and [SourceScan](https://github.com/SourceScan). :::info In order to make use of reproducible builds, you will need [Docker](https://docker.com) installed on your machine. ::: When initializing your project with `cargo near new`, the generated `Cargo.toml` will include information about the build environment and repository. ```toml # ... repository = "https://github.com//" # ... [package.metadata.near.reproducible_build] image = "sourcescan/cargo-near:0.13.5-rust-1.85.1" image_digest = "sha256:3b0272ecdbb91465f3e7348330d7f2d031d27901f26fb25b4eaf1560a60c20f3" passed_env = [] container_build_command = [ "cargo", "near", "build", "non-reproducible-wasm", "--locked", ] # ... ``` When deploying with `cargo near deploy`, this information is used to clone the repository and compile the contract in a Docker container. The compiling process will add the method `contract_source_metadata` to the contract **with no changes to the contract's code or logic** that will return the resulting metadata. After the contract is deployed, we can see the metadata by calling the method `contract_source_metadata`, e.g.: ```zsh near view contract_source_metadata INFO --- Result ------------------------- | { | "build_info": { | "build_command": [ | "cargo", | "near", | "build", | "non-reproducible-wasm", | "--locked" | ], | "build_environment": "sourcescan/cargo-near:0.13.5-rust-1.85.1@sha256:3b0272ecdbb91465f3e7348330d7f2d031d27901f26fb25b4eaf1560a60c20f3", | "contract_path": "", | "output_wasm_path": null, | "source_code_snapshot": "" | }, | "link": "", | "standards": [ | { | "standard": "nep330", | "version": "1.3.0" | } | ], | "version": "0.1.0" | | ------------------------------------ ``` ## Verify and Publish In order to verify and publish your contract's code, you can use [NearBlocks](https://nearblocks.io) to trigger the verification process. Navigate to the contract's account page and under the **Contract** tab, you will see the **Verify and Publish** button. After the verification process is completed, the contract's source code, along with the metadata, will be publicly accessible on NearBlocks on the `Contract -> Contract Code` tab. ![reproducible-build](/assets/docs/smart-contracts/reproducible-build.png) For a step-by-step guide on how to verify your contract, check out the [Verification Guide](https://github.com/SourceScan/verification-guide). ## Additional Resources - [Verifying Smart Contracts on NEAR: Step-by-Step Guide](https://github.com/SourceScan/verification-guide) - [Tools Community Call #10 - Introducing Reproducible Builds (video)](https://youtu.be/RBIAcQj7nFs?t=1742) For Python smart contracts, reproducibility is less of an issue since the Python SDK doesn't compile to WebAssembly directly. Instead, the Python code is executed in a Python-to-WASM interpreter that's embedded in the contract runtime. When you deploy a Python smart contract, you're deploying your Python source code directly (or in a lightly processed form), rather than a compiled binary. This makes the deployment process more deterministic, as there's less opportunity for build environment factors to influence the resulting artifact. ### Ensuring Reproducible Python Contracts To ensure your Python contracts are reproducible: 1. **Pin your dependencies**: If your contract uses any third-party libraries, make sure to specify exact versions. 2. **Use consistent formatting**: Use tools like Black or YAPF to ensure consistent code formatting. 3. **Document Python version**: Make sure to document which Python version your contract was developed with. 4. **Avoid system-specific code**: Don't rely on features that might behave differently across operating systems. --- # Source: https://docs.near.org/tutorials/multichain-dao/request.md --- id: request title: "Abstract DAO: Requests" description: "Learn how to create a signature request in Abstract DAO to execute actions on foreign EVM chains." --- The Abstract DAO contract works as an intermediary between decentralized organizations in NEAR and EVM networks. To better understand how it works it is better to start by using it by itself, without using a DAO yet. Join us as we explore how to create a request in the Abstract DAO contract, that will be later used to derive signatures for foreign EVM chains. :::tip We have deployed the Abstract DAO in two environments: 1. Testnet: `abstract-dao.testnet` 2. Dev (unstable): `dev.abstract-dao.testnet` ::: --- ## Ethereum Function Call Imagine that our organization agreed on changing a value in a simple [counter we deployed on Sepolia Ethereum](https://sepolia.etherscan.io/address/0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3), and now want to leave this intent in the Abstract DAO. For this, we will call the **`register_signature_request`** function on Abstract Dao saying: *We allow **executor.testnet** to request signatures for one of our Ethereum accounts, making it set a counter to `1000`*. Here are the parameters, take a quick glance for now, since we will go over each one of them: ```js { "request": { "derivation_seed_number": 0, "allowed_account_id": "executor.testnet", "transaction_payload": { "to": "0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3", "nonce": "0", "function_data": { "function_abi": { "inputs": [ { "internalType": "uint256", "name": "_num", "type": "uint256" } ], "name": "set", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, "arguments": [ { "Uint": "A97" } ] } }, } } ``` There are 3 arguments in the call above: `derivation_seed_number`, `transaction_payload`, and `allowed_account_id` lets see them in depth.
### Derivation Path The parameter `derivation seed number` will be used to derive which external address we will be requesting signatures from, the address will be derived as: `DAO Address` + `Derivation Path` + `Contract Address` = `EVM Address` For example, if we register a request from the address `...` using the derivation path `0` we will obtain control the `0x...` account. :::tip Learn more about derivation paths [here](../../chain-abstraction/chain-signatures.md) :::
### Transaction Payload The `transaction_payload` contains all the information on the transaction that we want to perform, particularly: - `to`: The recipient address of the transaction - `nonce`: The transaction nonce, used to ensure uniqueness - `function_data`: (optional) Defines the function that will be called on the recipient's contract, including: - `function_abi`: The ABI of the function being called - `arguments`: The input arguments for the function, all ABI encoded (e.g. integers are `base64`) There are a couple important points to notice about this `transaction_payload`: - **Readable Payload:** The parameters make it easy for anyone to quickly understand what transaction will be executed externally. The Abstract DAO is designed to be transparent and easy to audit, abstracting away the complexity of creating the transaction. - **We Are Setting a Nonce:** By setting the nonce, we make sure that the transaction will only be valid once, as future transactions will need higher `nonces` - **We Are Not Setting the GAS:** Gas prices are expected to vary wildly across EVMs, for which it makes no sense to setup a fixed gas amount and gas price for all the networks, for this is that we use the last parameter `allowed_account`
### Allowed Account In this case, the `allowed_account` will be the one in charge of generating the signatures. At the time of generating the signature, the account will also set the `gas_price` for the transaction on a per-chain basis. --- # Source: https://docs.near.org/chain-abstraction/omnibridge/roadmap.md --- id: roadmap sidebar_label: Roadmap title: Omni Bridge Roadmap description: "Explore the Omni Bridge roadmap, including hybrid architecture launch, Chain Signatures migration path, and future development plans for cross-chain infrastructure." --- Omni Bridge launches with a hybrid architecture, utilizing different verification methods based on chain-specific requirements and technical constraints. This approach allows us to support multiple chains from day one while progressively transitioning to full Chain Signatures integration. ## Supported Chains The bridge currently supports the following networks: - **EVM Chains:** - Ethereum - Base - Arbitrum - BNB Chain - Polygon (PoS) - **Non-EVM Chains:** - Bitcoin - Solana - Zcash ## Architecture Overview Omni Bridge utilizes **Chain Signatures** as its primary verification mechanism for outbound transfers from NEAR. Incoming transfers rely on chain-specific verification methods, including light clients for maximum security where available. ### Verification Methods - **Ethereum & Bitcoin:** Light Client verification for inbound transfers. - **Other Chains:** Message passing protocols verified by the NEAR network. - **Outbound (All Chains):** Chain Signatures (MPC) for secure transaction signing. ## Future Development 1. **Protocol Improvements** - Enhanced fee mechanisms - Cross-chain contract calls - New token standards support Beyond basic asset transfers, we're expanding the bridge's capabilities. Enhanced fee mechanisms will better handle gas price volatility, while cross-chain contract calls will enable more complex interactions. 2. **Infrastructure** - Expanded relayer network - Improved monitoring tools - Enhanced developer tooling Infrastructure development focuses on reliability and usability. An expanded relayer network improves transfer speeds and reliability, while better monitoring and developer tools make integration and maintenance easier. ## Get Involved ### Areas for Contribution - Chain integrations - Performance optimization - Security analysis - Developer tools - [Near-One/omni-bridge](https://github.com/Near-One/omni-bridge) - Omni Bridge repository - [Near-One/bridge-sdk-js](https://github.com/Near-One/bridge-sdk-js) - JavaScript SDK - [Near-One/bridge-sdk-rs](https://github.com/Near-One/bridge-sdk-rs) - Rust SDK and Bridge CLI The code is open source and we welcome contributions from the community. Whether you're interested in adding support for new chains, optimizing performance, or building developer tools, there's room for meaningful contribution. Bridge infrastructure is a fundamental component of a multi-chain future. Through Chain Signatures, we're creating a more efficient, secure, and scalable approach to cross-chain communication. Join us in building it. --- # Source: https://docs.near.org/data-infrastructure/tutorials/running-near-lake/run-lake-indexer.md --- id: run-lake-indexer title: Running Lake Indexer description: "Learn how to set up and run a NEAR Lake Indexer, including prerequisites, network configuration, and commands for syncing from the latest or a specific block." --- At NEAR we already have a working solution to index blockchain data and store it in AWS S3 buckets called **NEAR Lake**. In this guide you will learn how to set up and run an instance of the NEAR Lake Indexer. The Lake Indexer setup consists of the following components: - AWS S3 Bucket as a storage - NEAR Lake binary that operates as a regular NEAR Protocol peer-to-peer node, so you will operate it as any other [Regular/RPC Node in NEAR](https://near-nodes.io/rpc/hardware-rpc) :::info NEAR Lake is an indexer specialized on storing events as JSON files on AWS S3, built on top of the [NEAR Indexer microframework](https://github.com/nearprotocol/nearcore/tree/master/chain/indexer) :::
### Prepare Development Environment Before you proceed, make sure you have the following software installed: - [Rust compiler](https://rustup.rs/) of the version that is mentioned in `rust-toolchain` file in the root of [nearcore](https://github.com/nearprotocol/nearcore) project. - Ensure you have [AWS Credentials configured](https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-files.html) From AWS Docs: > For example, the files generated by the AWS CLI for a default profile configured with aws configure looks similar to the following. > > ~/.aws/credentials > > ``` > [default] > aws_access_key_id=AKIAIOSFODNN7EXAMPLE > aws_secret_access_key=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY > ```
### Compile NEAR Lake ```bash $ cargo build --release ```
### Configure NEAR Lake To connect NEAR Lake to the specific chain you need to have necessary configs, you can generate it as follows: ```bash $ ./target/release/near-lake --home ~/.near/testnet init --chain-id testnet --download-config --download-genesis ``` The above code will download the official genesis config and generate necessary configs. You can replace `testnet` in the command above to different network ID (`betanet`, `mainnet`). Configs for the specified network are in the `--home` provided folder. We need to ensure that NEAR Lake follows all the necessary shards, so `"tracked_shards"` parameters in `~/.near/testnet/config.json` needs to be configured properly. Currently, `nearcore` treats empty value for `"tracked_shards"` as "do not track any shard" and **any value** as "track all shards". For example, in order to track all shards, you just add the shard #0 to the list: ``` ... "tracked_shards": [0], ... ```
### Run NEAR Lake Commands to run NEAR Lake, after `./target/release/near-lake` | Command | Key/Subcommand | Required/Default | Responsible for | |---------|-------------------------------|------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | | `--home` | Default
`~/.near` | Tells the node where too look for necessary files:
`config.json`
,
`genesis.json`
,
`node_key.json`
, and
`data`
folder | | `init` | | | Tells the node to generate config files in `--home-dir` | | | `--chain-id` | Required

_ `localnet`
_ `testnet`
\* `mainnet` | Defines the chain to generate config files for | | | `--download-config` | Optional | If provided tells the node to download `config.json` from the public URL. You can download them manually

- [testnet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/testnet/rpc/config.json)
- [mainnet config.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/mainnet/rpc/config.json) | | | `--download-genesis` | Optional | If provided tells the node to download `genesis.json` from the public URL. You can download them manually

- [testnet genesis.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/testnet/genesis.json)
- [mainnet genesis.json](https://s3-us-west-1.amazonaws.com/build.nearprotocol.com/nearcore-deploy/mainnet/genesis.json) | | | TODO:
Other `neard` keys | | | | `run` | | | Runs the node | | | `--bucket` | Required | AWS S3 Bucket name | | | `--region` | Required | AWS S3 Bucket region | | | `--fallback-region` | Default eu-central-1 | AWS S3 Fallback region | | | `--endpoint` | Optional | AWS S3 compatible API endpoint | | | `--stream-while-syncing` | Optional | If provided Indexer streams blocks while they appear on the node instead of waiting the node to be fully synced | | | `--concurrency` | Default 1 | Defines the concurrency for the process of saving block data to AWS S3 | | | `sync-from-latest` | One of the `sync-` subcommands is required | Tells the node to start indexing from the latest block in the network | | | `sync-from-interruption` | One of the `sync-` subcommands is required | Tells the node to start indexing from the block the node was interrupted on (if it is a first start it will fallback to `sync-from-latest`) | | | `sync-from-block --height N` | One of the
`sync-`
subcommands is required | Tells the node to start indexing from the specified block height `N` (**Ensure** you node data has the block you want to start from) | ```bash $ ./target/release/near-lake --home ~/.near/testnet run --stream-while-syncing --concurrency 50 sync-from-latest ``` After the network is synced, you should see logs of every block height currently received by NEAR Lake. --- ## Syncing Whenever you run NEAR Lake for any network except localnet you'll need to sync with the network. This is required because it's a natural behavior of `nearcore` node and NEAR Lake is a wrapper for the regular `nearcore` node. In order to work and index the data your node must be synced with the network. While snapshot-based syncing was previously the recommended default, we now recommend [Epoch Sync](https://near-nodes.io/intro/node-epoch-sync) —a faster, more lightweight method that allows a node to catch up from genesis without downloading a large state snapshot. --- ## Running NEAR Lake as an archival node It's not necessary but in order to index everything in the network it is better to do it from the genesis. `nearcore` node is running in non-archival mode by default. That means that the node keeps data only for [5 last epochs](/protocol/network/epoch). In order to index data from the genesis you need to turn the node in archival mode. To do it you need to update `config.json` located in `--home-dir` (by default it is `~/.near`). Find next keys in the config and update them as following: ```json { ... "archive": true, "tracked_shards": [0], ... } ``` The syncing process in archival mode can take a lot of time, so it's better to download a backup provided by NEAR and put it in your `data` folder. After that your node will download only the data after the backup was cut and it takes reasonable amount time. All the backups can be downloaded from the public [S3 bucket](https://docs.fastnear.com/docs/snapshots/mainnet#archival-mainnet-snapshot) provided by FastNEAR. See [this link](https://near-nodes.io/archival/run-archival-node-with-nearup) for reference --- ## Using the data We write all the data to AWS S3 buckets: - `near-lake-data-testnet` (`eu-central-1` region) for testnet - `near-lake-data-mainnet` (`eu-central-1` region) for mainnet --- ## Custom S3 storage In case you want to run you own `near-lake` instance and store data in some S3 compatible storage ([Minio](https://min.io/) or [Localstack](https://localstack.cloud/) as example) You can override default S3 API endpoint by using `--endpoint` option - run `minio` ```bash $ mkdir -p /data/near-lake-custom && minio server /data ``` - run `near-lake` ```bash $ ./target/release/near-lake --home ~/.near/testnet run --endpoint http://127.0.0.1:9000 --bucket near-lake-custom sync-from-latest ```
### Data structure The data structure we use is the following: ``` / block.json shard_0.json shard_1.json ... shard_N.json ``` - `` is a 12-character-long `u64` string with leading zeros (e.g `000042839521`). [See this issue for a reasoning](https://github.com/near/near-lake/issues/23) - `block_json` contains JSON-serialized [`BlockView`](https://github.com/near/nearcore/blob/e9a28c46c2bea505b817abf484e6015a61ea7d01/core/primitives/src/views.rs#L711-L716) struct. **Note:** this struct might change in the future, we will announce it - `shard_N.json` where `N` is `u64` starting from `0`. Represents the index number of the shard. In order to find out the expected number of shards in the block you can look in `block.json` at `.header.chunks_included`
### Access the data All NEAR Lake AWS S3 buckets have [Request Payer](https://docs.aws.amazon.com/AmazonS3/latest/userguide/RequesterPaysBuckets.html) enabled. It means that anyone with their own AWS credentials can List and Read the bucket's content and **be charged for it by AWS**. Connections to the bucket have to be done with AWS credentials provided. See [NEAR Lake Framework](https://github.com/near/near-lake-framework) for a reference.
### NEAR Lake Framework Once we [set up the public access to the buckets](https://github.com/near/near-lake/issues/22) anyone will be able to build their own code to read it through. For our own needs we are working on [NEAR Lake Framework](https://github.com/near/near-lake-framework) to have a simple way to create an indexer on top of the data stored by NEAR Lake itself. :::note See the official NEAR Lake Framework [announcement on the NEAR Gov Forum](https://gov.near.org/t/announcement-near-lake-framework-brand-new-word-in-indexer-building-approach/17668). ::: --- # Source: https://docs.near.org/web3-apps/tutorials/localnet/run.md --- id: run title: Run Your Own Localnet description: "Learn how to run a localnet on NEAR." --- Now that we understand what a localnet is, let's walk through the complete workflow of running it and testing it with a simple smart contract and a frontend application. For this tutorial, we'll use `near-sandbox`, since it's the easiest and fastest option to start with. Our goal for this tutorial is to set up a local network, create a wallet account on it, deploy a contract, and finally run [a frontend application](https://github.com/near-examples/hello-localnet) that interacts with this contract directly through the local blockchain. :::tip Also check the [localnet tutorial](https://github.com/near/mpc/blob/main/docs/localnet/localnet.md) by the NEAR MPC team ::: ### Prerequisites Before we begin, make sure you have a few essential tools installed. - [near-cli-rs](https://github.com/near/near-cli-rs) - the CLI for interacting with the NEAR blockchain; - [cargo-near](https://github.com/near/cargo-near) - the CLI that simplifies building/deploying Rust smart contracts. ### Walkthrough #### Step 1. Setup and boot up sandbox node First, run the following command: ```sh npx near-sandbox --home /tmp/near-sandbox init ``` This command prepares a fresh configuration inside the home folder, which is `/tmp/near-sandbox` in the command above. Once it's finished, you'll find several files created in that directory: - `config.json` - main node configuration, defining network ports, storage paths, runtime settings, etc - `genesis.json` - defines the initial blockchain state, including default account `near` and the validator account (by default, it's `test.near`) - `validator_key.json` - validator's keypair used to produce blocks - `node_key.json` - keypair used for signing network packets This initialization process is a one-time setup. Once the configuration is in place, you can start and stop the node as many times as you like, the blockchain state will persist inside this folder. After initialization, start the node: ```sh npx near-sandbox --home /tmp/near-sandbox run ``` The node will boot up and begin producing blocks locally. :::tip To confirm that your node is actually running, you can query its `status` RPC endpoint with the following command `curl http://127.0.0.1:3030/status`. ::: #### Step 2. Import `test.near` account In this step, we'll import the default `test.near` account that was created during `near-sandbox` initialization. We'll need this account later to deploy our smart contract, so let's prepare it now. First, make sure your `near-cli-rs` knows how to connect to your local network. If you already have a `localnet` configuration, you can skip this step. Otherwise, run: ```sh near config add-connection --network-name localnet --connection-name localnet-sandbox --rpc-url http://localhost:3030/ --wallet-url http://localhost:3030/ --explorer-transaction-url http://localhost:3030/ ``` During this process, the CLI may ask you a few interactive questions. You can safely answer "No" to all of them - all required values are already defined in the command above. :::tip To confirm that the connection was set up correctly, you can view information about the `test.near` account with the following command `near account view-account-summary test.near network-config localnet-sandbox now`. ::: When you initialized the `near-sandbox`, it created a file named `validator_key.json` inside the home directory. This file contains the secret key for the `test.near` account, which we'll need to import into `near-cli-rs`. To view the key, run: ```sh cat /tmp/near-sandbox/validator_key.json ``` You'll see output similar to this: ```json { "account_id": "test.near", "public_key": "ed25519:...", "secret_key": "ed25519:..." } ``` Copy the `secret_key` value and import it into `near-cli-rs` by running the following command (replace `` with the actual key): ```sh near account import-account using-private-key network-config localnet-sandbox ``` When you run the command above, you'll be prompted to enter the account ID. Type `test.near` and press Enter. You'll then be asked to choose a keychain for storing the credentials, select `Legacy Keychain`, as it stores the keys directly in the file system, which is the simplest and the most convenient option for `localnet`. #### Step 3. Deploy the smart contract In this step, we'll deploy a simple [Hello World smart contract](https://github.com/near-examples/hello-near-examples) to our `localnet`. Before deploying, we need to create an accont to which a contract will be deployed. Since we've already imported the `test.near` account, we can use it as the parent account to create a new subaccount: ```sh near account create-account fund-myself hello-world.test.near '10 NEAR' autogenerate-new-keypair save-to-legacy-keychain sign-as test.near network-config localnet-sandbox sign-with-legacy-keychain send ``` This command creates a new account named `hello-world.test.near` with an initial balance of 10 NEAR. Next, clone the Hello World repository and navigate to the contract directory: ```sh git clone git@github.com:near-examples/hello-near-examples.git cd hello-near-examples/contract-rs ``` Once you're inside the repository, run the following command: ```sh cargo near deploy build-non-reproducible-wasm hello-world.test.near without-init-call network-config localnet-sandbox sign-with-legacy-keychain send ``` This command compiles the smart contract, optimizes the resulting `.wasm` file, and deploys it directly to the `hello-world.test.near` account on `localnet`. :::tip To verify that the contract was deployed successfully, you can inspect the code of the `hello-world.test.near` account with the following command `near contract inspect hello-world.test.near network-config localnet-sandbox now`. If the deployment worked, you'll see a list of the contract's methods printed in the output, like `get_greeting` and `set_greeting`. ::: #### Step 4. Create user's wallet account In this step, we'll create a user account inside a wallet that can connect to our `localnet`. This is important because frontend applications typically interact with contracts through a wallet connection. Not every NEAR wallet supports `localnet` connections, but for this tutorial, we'll use Intear Wallet, which is fully compatible.\ Navigate to https://wallet.intear.tech. If you don't have an account yet, create one or import any existing account. It doesn't matter which one, this step is just to get you inside the wallet interface. Once you're in the wallet, open the `Settings` panel from the sidebar, then find the `Developer` tab. You'll see a `Localnet` subsection where you can add your own local network configuration. Click `Add`, and fill in the fields as follows: set the RPC URL to `http://127.0.0.1:3030` and the Network ID to `localnet`. Then click `Save`. If everything is configured correctly, you should see a small, green `Online` badge appear next to the new network indicating that it's connected. Here's how it should look like: ![Preview](/assets/docs/web3-apps/intear-wallet-localnet-connected.png) Next, click the `Control` button on the right side of the `localnet` entry. Find the `Create New Account section`, type a name for your new account (in our case, it's `user.local`) and click `Create`. Once the request goes through, the new account will appear in your wallet. At this point, you have a valid account on your `localnet` that can sign transactions through Intear Wallet. #### Step 5. Spin up and interact with frontend application Start by cloning the repository: ```sh git clone https://github.com/near-examples/hello-localnet cd hello-localnet ``` Once you're inside the repository, install the dependencies using `pnpm`: ```sh pnpm install --frozen-lockfile ``` Then, start the development Next.js server: ```sh pnpm dev ``` Navigate to http://localhost:3000/hello-near, and at the top-right corner, click `Login`. When prompted to select a wallet, choose `Intear Wallet`, Web version. And make sure you connect using the account created earlier, in our case, it's `user.local.` Once connected, you'll see an input field and a button to update the greeting. Type any new greeting into the input and click `Save`. You'll be redirected to the wallet to approve a transaction. Once approved, the transaction will be sent and executed on `localnet` node. Return to the frontend and notice that the greeting has changed. --- You've now gone through the entire process of setting up a local NEAR environment — from running the Sandbox and deploying a contract to connecting a wallet and interacting with it through a frontend. With this setup, you can experiment freely, test your contracts safely, and build end-to-end decentralized applications without touching mainnet or testnet. --- # Source: https://docs.near.org/protocol/network/runtime.md --- id: runtime title: Runtime description: "Explore NEAR Protocol's runtime system, including core runtime operations, cross-contract calls, action and data receipts, and state management." --- This section contains videos that explore the core Runtime, its operation, and how it implements cross-contract calls. ## Runtime Overview An in-depth code overview of NEAR Runtime. ## Runtime Action and Data Receipts An in-depth code review of how NEAR Runtime implements cross contract calls. ## Runtime State An in-depth overview of how NEAR runtime operates with its state. --- # Source: https://docs.near.org/tutorials/auction/sandbox-testing.md --- id: sandbox-testing title: Sandbox Testing description: "Lets test our contract in a realistic sandbox environment." --- In the previous section, we went through the contract's code, analyzing how it worked. Now, we need to test it and make sure it works as expected! For contracts, there are two types of testing you can do: unit testing and sandbox testing. Here, we will focus on sandbox testing, as it enables one to deploy the contract in a realistic environment, allowing us to create multiple accounts and interact with the contract as if it was deployed on the blockchain. :::info unit testing Unit tests are built into the language and are used to test the contract functions individually. These tests work well when little context is required. However, they cannot test chain interactions - like sending accounts $NEAR tokens - since they need to be processed by the network. ::: --- ## Account Creation The first thing our test does is to create multiple accounts with 10 $NEAR tokens each and deploy the contract to one of them. ``` const worker = t.context.worker = await Worker.init(); // Create accounts const root = worker.rootAccount; const alice = await root.createSubAccount("alice", { initialBalance: NEAR.parse("10 N").toString() }); const bob = await root.createSubAccount("bob", { initialBalance: NEAR.parse("10 N").toString() }); const auctioneer = await root.createSubAccount("auctioneer", { initialBalance: NEAR.parse("10 N").toString() }); const contract = await root.createSubAccount("contract", { initialBalance: NEAR.parse("10 N").toString() }); // Deploy contract (input from package.json) await contract.deploy(process.argv[2]); ``` To deploy the contract, we pass the path to the compiled WASM contract as an argument to the test in `package.json`. Indeed, when executing `npm run test`, the command will first compile the contract and then run the tests. ``` // Initialize the sandbox let sandbox = near_sandbox::Sandbox::start_sandbox().await?; let sandbox_network = near_api::NetworkConfig::from_rpc_url("sandbox", sandbox.rpc_addr.parse()?); // Create accounts let alice = create_subaccount(&sandbox, "alice.sandbox").await?; let bob = create_subaccount(&sandbox, "bob.sandbox").await?; let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; let contract = create_subaccount(&sandbox, "contract.sandbox") .await? .as_contract(); // Initialize signer for the contract deployment ``` Notice that the sandbox compiles the code itself, so we do not need to pre-compile the contract before running the tests. --- ## Contract Initialization To initialize, the contract's account calls itself, invoking the `init` function with an `end_time` set to 60 seconds in the future. ``` await contract.call(contract, "init", { end_time: String((Date.now() + 60000) * 10 ** 6), auctioneer: auctioneer.accountId, }); ``` :::warning Time Units The contract measures time in **nanoseconds**, for which we need to multiply the result of `Date.now()` (expressed in milliseconds) by `10^6` ::: ``` let signer = near_api::Signer::from_secret_key( near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY .parse() .unwrap(), )?; // Calculate the end time for the auction as a parameter for the init function let now = std::time::SystemTime::now() .duration_since(std::time::SystemTime::UNIX_EPOCH)? ``` :::warning Time Units The contract measures time in **nanoseconds**, for which we need to multiply the result of `Utc::now().timestamp()` (expressed in seconds) by `10^9` ::: :::info Time is a String Notice that the time is passed as a `String` to the contract, this is because smart contracts cannot receive numbers larger than `52 bits` and we want to pass a `unix timestamp` in **nanoseconds** ::: --- ## Bidding Now that the contract is deployed and initialized, we can start bidding and checking if the contract behaves as expected. We first make `alice` place a bid of 1 NEAR, and check that the contract correctly registers the bid. Then, we have `bob` place a bid of 2 NEAR, and check that the highest bid is updated, and that `alice` gets her NEAR refunded. ``` // Alice makes first bid await alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() }); let highest_bid = await contract.view("get_highest_bid", {}); t.is(highest_bid.bidder, alice.accountId); t.is(highest_bid.bid, NEAR.parse("1 N").toString()); const aliceBalance = await alice.balance(); // Bob makes a higher bid await bob.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("2 N").toString() }); highest_bid = await contract.view("get_highest_bid", {}); t.is(highest_bid.bidder, bob.accountId); t.is(highest_bid.bid, NEAR.parse("2 N").toString()); // Check that alice was returned her bid const aliceNewBalance = await alice.balance(); t.deepEqual(aliceNewBalance.available, aliceBalance.available.add(NEAR.parse("1 N"))); ``` ``` // Deploy the contract with the init call near_api::Contract::deploy(contract.account_id().clone()) .use_code(contract_wasm) .with_init_call( "init", json!({"end_time": a_minute_from_now.to_string(), "auctioneer": auctioneer.account_id()}), )? .with_signer(signer.clone()) .send_to(&sandbox_network) .await? .assert_success(); // Alice makes first bid contract .call_function("bid", ()) .transaction() .deposit(NearToken::from_near(1)) .with_signer(alice.account_id().clone(), signer.clone()) .send_to(&sandbox_network) .await? .assert_success(); // For now, the highest bid is the Alice's bid let highest_bid: Bid = contract .call_function("get_highest_bid", ()) .read_only() .fetch_from(&sandbox_network) .await? .data; assert_eq!(highest_bid.bid, NearToken::from_near(1)); assert_eq!(&highest_bid.bidder, alice.account_id()); let alice_balance = alice ``` #### Checking the balance It is important to notice how we check if `alice` was refunded. We query her balance after her first bid, and then check if it has increased by 1 NEAR after `bob` makes his bid. You might be tempted to check if `alice`'s balance is exactly 10 NEAR after she gets refunded, but `alice` balance cannot be 10 NEAR anymore, because some $NEAR was **consumed as `gas` fees** when `alice` called `bid`. #### Testing invalid calls When testing we should also check that the contract does not allow invalid calls. The next part checks that the contract doesn't allow for bids with fewer $NEAR tokens than the previous to be made. ``` await t.throwsAsync(alice.call(contract, "bid", {}, { attachedDeposit: NEAR.parse("1 N").toString() })); ``` ``` .near_balance() .fetch_from(&sandbox_network) .await? .total; // Now, Bob makes a higher bid contract .call_function("bid", ()) ``` --- ## Fast Forwarding Time The sandbox allows us to fast-forward time, which is useful for testing the contract when the auction is over. The test advances 200 blocks in order to pass a minute, and thus allowing the auction to be claimed. After which the auction can now be claimed. Once claimed the test checks that the auctioneer has received the correct amount of $NEAR tokens. ``` // Fast forward 200 blocks await t.context.worker.provider.fastForward(200) const auctioneerBalance = await auctioneer.balance(); const available = parseFloat(auctioneerBalance.available.toHuman()); // Auctioneer claims the auction await auctioneer.call(contract, "claim", {}, { gas: "300000000000000" }); // Checks that the auctioneer has the correct balance const contractNewBalance = await auctioneer.balance(); const new_available = parseFloat(contractNewBalance.available.toHuman()); t.is(new_available.toFixed(1), (available + 2).toFixed(1)); ``` ``` .read_only() .fetch_from(&sandbox_network) .await? .data; assert_eq!(highest_bid.bid, NearToken::from_near(2)); assert_eq!(&highest_bid.bidder, bob.account_id()); // Check that Alice was refunded her bid let new_alice_balance = alice .tokens() .near_balance() .fetch_from(&sandbox_network) .await? .total; assert!(new_alice_balance == alice_balance.saturating_add(NearToken::from_near(1))); // Alice tries to make a bid with less NEAR than the previous contract .call_function("bid", ()) ``` If you review the tests in full you'll see that we also test other invalid calls such as the auctioneer trying to claim the auction before it is over and a user attempting to bid once the auction is over. --- ## Executing the tests Now that we understand what we are testing, let's go ahead and run the tests! ```bash # if you haven't already, install the dependencies npm install # run the tests npm run test ``` ```bash cargo test ``` All tests should pass, and you should see the output of the tests in the console. If you see any errors, please contact us in the [NEAR Discord](https://near.chat) or through [Telegram](https://t.me/neardev) and we'll help you out! --- ## Conclusion In this part of the tutorial, we've seen how to use our sandbox testing environment to test the contract. We've tested the contract's initialization, bidding, and time advancement. You are now ready to move to the [next section](./1.3-deploy.md), where we will deploy the contract to `testnet` and interact with it through the CLI. --- # Source: https://docs.near.org/primitives/ft/sdk-contract-tools.md --- id: sdk-contract-tools title: Create FT using Contract Tools sidebar_label: Create FT using Contract Tools description: "Learn how to create a fungible token (FT) using Contract Tools package" --- In this tutorial, we will create a fungible token (FT) using the [NEAR SDK Contract Tools](https://github.com/near/near-sdk-contract-tools) package. This package is a collection of common tools and patterns to simplify smart contract development, including: - Storage fee management - Escrow pattern and derive macro - Owner pattern and derive macro - Pause pattern and derive macro - Role-based access control - Derive macros for [NEP standards](./standard.md) - NEP-141 (fungible token), extension NEP-148 - NEP-145 (storage management), and integrations for the fungible token and non-fungible token standards - NEP-171 (non-fungible token), extensions NEP-177, NEP-178, NEP-181 - NEP-297 (events) --- ## Introduction While one can create a fungible token (FT) contract from scratch using only the `near-sdk` and `near_contract_standards` (e.g. [FT contract](https://github.com/near-examples/FT)), a simpler approach is to use `near-sdk-contract-tools`. `near-sdk-contract-tools` allows us implement the logic for minting/burning logic, access control, and other FT standards by simply deriving macros on our contract struct, as `OpenZeppelin` does for Ethereum contracts. --- ## Basic FT Methods To derive basic FT methods to our contract, we need to derive `FungibleToken` macro to our contract struct: ``` #[derive(Default, Owner, FungibleToken)] #[fungible_token(transfer_hook = "TransferHook")] #[near(contract_state)] pub struct MyFtContract {} ``` This will bring all the basic FT methods defined in NEP-141 standard to our contract: - `new` - `contract_source_metadata` - `ft_balance_of` - `ft_metadata` - `ft_total_supply` - `storage_balance_bounds` - `storage_balance_of` - `ft_resolve_transfer` - `ft_transfer` - `ft_transfer_call` - `storage_deposit` - `storage_unregister` - `storage_withdraw` To bring basic owner methods to our contract, we can also derive the `Owner` macro which adds the following methods: - `own_get_owner` - `own_get_proposed_owner` - `own_accept_owner` - `own_propose_owner` - `own_renounce_owner` --- ## Initialization To initialize the basic FT contract with custom owner, metadata and storage bounds implement `new` method: ``` #[near] impl MyFtContract { #[init] pub fn new( owner_id: AccountId, /* total_supply: U128, */ metadata: ContractMetadata, ) -> Self { let mut contract = Self {}; // Set metadata contract.set_metadata(&metadata); // Initialize owner Owner::init(&mut contract, &owner_id); contract.set_storage_balance_bounds(&StorageBalanceBounds { min: NearToken::from_yoctonear(2500000000000000000000), max: Some(NearToken::from_yoctonear(2500000000000000000000)), }); /* This contract wraps the deposited NEAR tokens, so it should have an initial supply of 0. However, if you want your contract to start with an initial FT supply, you can uncomment the following line and pass total_supply to the new method. let _ = contract.deposit_unchecked(&owner_id, total_supply.0); */ contract } } ``` --- ## Transfer Hook If we want to customize how the transfer of tokens work (i.e. modify the `ft_transfer` method), we need to implement a hook. Hooks are a way to **wrap (inject code before and after)** component functions: ``` pub struct TransferHook; impl Hook> for TransferHook { fn hook( contract: &mut MyFtContract, transfer: &Nep141Transfer<'_>, f: impl FnOnce(&mut MyFtContract) -> R, ) -> R { // Log, check preconditions, save state, etc. log!( "NEP-141 transfer from {} to {} of {} tokens", transfer.sender_id, transfer.receiver_id, transfer.amount ); let storage_usage_before = env::storage_usage(); let r = f(contract); // execute wrapped function let storage_usage_after = env::storage_usage(); log!( "Storage delta: {}", storage_usage_after - storage_usage_before ); r } } ``` Then derive it to our contract struct: ``` #[derive(Default, Owner, FungibleToken)] #[fungible_token(transfer_hook = "TransferHook")] #[near(contract_state)] pub struct MyFtContract {} ``` --- ## Minting By default, the FT standards do not include a minting method. However, we can easily mint tokens for the owner by implementing a `mint` method, which only the owner can call: ``` #[near] impl MyFtContract { #[payable] pub fn mint(&mut self) { let mut amount_to_mint = env::attached_deposit(); // Check account's storage balance and deposit if necessary let storage_balance_bounds = self.get_storage_balance_bounds(); let storage_balance = self .get_storage_balance(&env::predecessor_account_id()) .unwrap_or_else(|_| StorageBalance::default()); if storage_balance.total < storage_balance_bounds.min { // Deposit storage if necessary self.storage_deposit(Some(env::predecessor_account_id()), None); amount_to_mint = amount_to_mint.saturating_sub(storage_balance_bounds.min); } // Mint tokens Nep141Controller::mint( self, &Nep141Mint { amount: amount_to_mint.as_yoctonear(), receiver_id: env::predecessor_account_id().into(), memo: None, }, ) .unwrap_or_else(|e| env::panic_str(&e.to_string())); } } ``` :::tip You can modify this method as you need, for example, to allow minting only when the contract is not paused (requires deriving [`Pausable`](https://github.com/near/near-sdk-contract-tools/tree/develop?tab=readme-ov-file#macro-combinations) hook), or to allow minting only to specific accounts with a certain role or from whitelist with custom limitations ::: --- ## Burning In the same way that minting is not included in the FT standards, burning is also not included. However, we can also easily implement it. To burn tokens from the owner's account, we can add a `burn` method which is also only callable by the owner: ``` #[near] impl MyFtContract { #[payable] pub fn burn(&mut self, amount: U128) { // Assert that the attached deposit is exactly 1 yocto NEAR assert_one_yocto(); // Burn tokens Nep141Controller::burn( self, &Nep141Burn { amount: amount.0, owner_id: env::predecessor_account_id().into(), memo: None, }, ) .unwrap_or_else(|e| env::panic_str(&e.to_string())); let amount_to_refund = NearToken::from_yoctonear(amount.0); Promise::new(env::predecessor_account_id()).transfer(amount_to_refund); } } ``` --- ## Conclusion Using `near-sdk-contract-tools` is a very simple and flexible way to create FT contract with minimal boilerplate which allows us to focus on the business logic. You can further extend this contract with more features like pausing, role-based access control, escrow pattern, and more by deriving corresponding macros from the package. --- # Source: https://docs.near.org/tools/sdk.md --- id: sdk title: NEAR SDK description: "Choose an SDK to start building contracts." --- The NEAR SDK is a library that allow to develop smart contracts. Currently, there exist two versions of NEAR SDK: one for Rust and one for JavaScript. :::tip Want to build a smart contract? The best place to start learning is our [QuickStart Guide](../smart-contracts/quickstart.md) ::: --- ## Smart Contracts on NEAR This is how a smart contract written in Rust and JavaScript using the NEAR SDK looks like: ```js @NearBindgen({}) class HelloNear { greeting: string = 'Hello'; @view({}) // This method is read-only and can be called for free get_greeting(): string { return this.greeting; } @call({}) // This method changes the state, for which it cost gas set_greeting({ greeting }: { greeting: string }): void { near.log(`Saving greeting ${greeting}`); this.greeting = greeting; } } ``` ```rust #[near(contract_state)] pub struct Contract { greeting: String, } impl Default for Contract { fn default() -> Self { Self { greeting: "Hello".to_string(), } } } #[near] impl Contract { pub fn get_greeting(&self) -> String { self.greeting.clone() } pub fn set_greeting(&mut self, greeting: String) { self.greeting = greeting; } } ``` :::tip Want to build a smart contract? Check our [QuickStart Guide](../smart-contracts/quickstart.md) ::: --- ## 🎉 Ready to start developing? Start from our [Smart Contract QuickStart Guide](../smart-contracts/quickstart.md), and let it guide you through all our documentation on building smart contracts --- ## Want to See Examples? 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 If you are new to smart contracts, we recommend you start with our [Smart Contract QuickStart Guide](../smart-contracts/quickstart.md) before moving to the examples ::: --- ## Searching for the Reference Docs If you need to find a specific function signature, or understand the SDK struct/classes, please visit the SDK specific pages: - [Rust SDK](https://docs.rs/near-sdk/latest/near_sdk/) - [JavaScript SDK](https://near.github.io/near-sdk-js/) :::tip If you are new to smart contracts, we recommend you start with our [Smart Contract QuickStart Guide](../smart-contracts/quickstart.md) before moving to the reference documentation ::: --- # Source: https://docs.near.org/ai/shade-agents/concepts/security.md --- id: security title: Security Considerations sidebar_label: Security Considerations description: "Learn key security practices when deploying Shade Agents, including duplicate actions, transaction restrictions, and RPC trust." --- :::warning The Shade Agent Framework is experimental and contains known critical vulnerabilities. It must not be used in production or on mainnet and is intended solely for testing and non-critical use on testnet. No representations or warranties are made regarding security, correctness, or fitness for any purpose. Use of this software is entirely at your own risk. A production-ready version of the framework is currently in development. ::: Please review this list of security considerations before deploying a Shade Agent to Mainnet. --- ## Restricting Actions While TEEs are considered trusted and tamper-resistant, implementing tight restrictions or `guard rails` on agent actions within the agent contract is recommended so that even if the private key to a agent was extracted funds can't be withdrew. Examples of restrictions could be: - The agent contract can only build transactions for a list of functions and smart contract addresses. - The agent contract can only build transactions for specific chains. - The agent contract can only build transactions that swap or send up to 0.1 ETH per 24 hours. We recommend using the [omni-transactions-rs](https://github.com/near/omni-transaction-rs) library to build transactions within your agent contract rather than in the agent. Another solution is for the Shade Agent's multichain accounts to not hold funds themselves, but the accounts to be a restricted admin on a contract on the target chain. --- ## Secure Data Centers With recent exploits of TEEs, such as TEE.fail, it’s important for TEEs to have physical security by running them in secure data centers. In addition to verifying agent attestations in the smart contract, it’s recommended to only allow them to register as valid agents (via whitelisting account IDs) after verifying off-chain that they are running within secure data centers (like ones provided by Phala Cloud). Proof of Cloud https://proofofcloud.org/ is an initiative to verify TEEs are running in secure facilities. --- ## Duplicate Actions To ensure liveness, the Shade Agent should consist of multiple identical agents hosted by different providers/on different hardware. When multiple agents are running, all agents will respond to updates (user inputs, agent pings, API updates, etc.) or will create the same action whilst running on a cron. You must ensure that the Shade Agent collectively performs the desired action only `once` in response to each update. Consider a Kaito Mindshare Trading Agent as an example. If Solana's mindshare increases relative to NEAR, the agent would swap SOL for NEAR. With two agents running, you must ensure this swap doesn't occur twice. This logic is typically best implemented within the agent contract or a target smart contract being interacted with. --- ## Failed or Unsent Transactions A successful MPC signature on a transaction payload doesn't guarantee the transaction's success or transmission. Transactions may fail for various reasons, such as network congestion, incorrect nonce, or insufficient gas. We suggest you build your agent in such a way that if the transaction fails, then a new signature can be requested without allowing for signing when the transaction is successful. For some use cases, it may be beneficial to emit signed transactions from your agent contract, allowing anyone or an indexer to relay them if your agent fails to retrieve the result. Signed transactions can be built using [omni-transactions-rs](https://github.com/near/omni-transaction-rs). Exercise caution with unsent signatures. --- ## Parallel Transactions Building on the above, the MPC signing a transaction payload doesn't change any nonce or state on the destination chain. So if you'd like to create an agent that can issue several parallel transactions, you will have to have an intimate knowledge of the destination chain and increment nonces for the derived accounts that you're generation signatures for. This can cause a lot of issues in the EVM world, for example: Transactions can get stuck in the mempool if the nonce used is not in sequential order. A transaction with a higher nonce will remain pending if a previous transaction (with a lower nonce) hasn't been processed. If your agent frequently tries to sign multiple transactions in parallel with the MPC, then you must keep track of which transactions are successful and handle failures appropriately so that all transactions eventually are included in blocks of the destination chain. --- ## Restricting Agent API Routes In the quickstart, our agent can take an API request from anyone. In a lot of cases, a Shade Agent will run on a loop responding to actions or data every set time period. If using API routes, you need to design your agent to only perform the action when the desired condition is met or do some sort of verification or authentication inside the route, for example, a user has signed an action with their wallet, or they are logged in from their Google account. --- ## Removing Agent Contract Keys Before deploying your agent contract to production, you should ensure that all access keys to the agent contract account have been removed. Otherwise, this would allow the access key owner to withdraw the funds held by the Shade Agent. It is best practice to implement an [upgrade mechanism](../../../tutorials/examples/update.md) that is controlled by a multisig or DAO. --- ## Trusting RPCs Inside an agent, it is common to want to query the state of the blockchain and perform actions based on the state. Since RPCs can lie about the state of the blockchain and do not have crypto-economic security, we suggest you design your agent to defend against this. Below are some solutions, which solution you use will differ depending on your use case and the design of your agent: #### Verifying the State In some cases, you will be able to submit the state back to the smart contract from which the state was queried and verify that it matches the actual state. For example, take a DAO on Ethereum that uses AI to vote on proposals. When a proposal goes live, the agent indexes the proposal, makes a decision on the proposal using an LLM, and then submits the yes/no decision back to the DAO. The agent should also submit a hash of the proposal back to the DAO, so the DAO can verify that the decision was made based on the true state of the blockchain. #### Trustless Providers You can use RPC providers that leverage cryptographic proofs or run in TEEs themselves to know that the result is the true state of the blockchain. #### Multiple Reputable Providers You can use multiple reputable RPC providers (like Infura and Alchemy) and check the result across each provider to make sure they match. --- # Source: https://docs.near.org/smart-contracts/anatomy/serialization-protocols.md --- id: serialization-protocols title: Serialization Protocols description: "Learn which protocols smart contracts use to serialize data." --- Serialization formats within the SDK define how data structures are translated into bytes which are needed for passing data into methods of the smart contract or storing data in state. For the case of method parameters, [JSON](https://www.json.org/json-en.html) (default) and [Borsh](https://borsh.io/) are supported with the SDK and for storing data on-chain Borsh is used. The qualities of JSON and Borsh are as follows: JSON: - Human-readable - Self-describing format (don't need to know the underlying type) - Easy interop with JavaScript - Less efficient size and (de)serialization Borsh: - Compact, binary format that's efficient for serialized data size - Need to know data format or have a schema to deserialize data - Strict and canonical binary representation - Fast and less overhead in most cases In general, JSON will be used for contract calls and cross-contract calls for a better DevX, where Borsh can be used to optimize using less gas by having smaller parameter serialization and less deserialization computation within the contract. ### Overriding Serialization Protocol Default The result and parameter serialization can be opted into separately, but all parameters must be of the same format (can't serialize some parameters as borsh and others as JSON). An example of switching both the result and parameters to borsh is as follows: ```rust #[result_serializer(borsh)] pub fn sum_borsh(#[serializer(borsh)] a: u32, #[serializer(borsh)] b: u32) -> u32 { a + b } ``` Where the `result_serializer(borsh)` annotation will override the default result serialization protocol from JSON to borsh and the `serializer(borsh)` annotations will override the parameter serialization. #### Example A simple demonstration of getting a [Borsh-serialized](https://borsh.io), base64-encoded value from a unit test: ``` /// This test is simply a helper to print out the base64 value. #[test] fn borsh_simple() { let status_message = "Aloha honua!".to_string(); let borsh_input = SetMessageInput { msg: status_message.clone() }; let borsh_serialized: Vec = borsh_input.try_to_vec().unwrap(); let base64_encoded = near_primitives::serialize::to_base64(borsh_serialized.as_slice()); println!("Using NEAR CLI, this is the base64-encoded value to use: {:?}", base64_encoded); } } ``` The following snippet shows a simple function that takes this value from a frontend or CLI. Note: this method doesn't have a return value, so the `#[result_serializer(borsh)]` isn't needed. ``` pub fn set_status_borsh(&mut self, #[serializer(borsh)] message: SetMessageInput) { self.records.insert(&env::signer_account_id(), &String::from(message.msg)); } } ``` Note that this is using this simple struct: ``` #[derive(BorshDeserialize, BorshSerialize)] pub struct SetMessageInput { // Note that the key does not have to be "message" like the argument name. msg: String, } ``` To call this with NEAR CLI, use a command similar to this: ```bash near contract call-function as-transaction rust-status-message.demo.testnet set_status_borsh base64-args 'DAAAAEFsb2hhIGhvbnVhIQ==' prepaid-gas '30 TeraGas' attached-deposit '0 NEAR' sign-as demo.testnet network-config testnet sign-with-keychain send ``` See more details in [this GitHub gist](https://gist.github.com/mfornet/d8a94af333a68d67affd8cb78464c7c0) from [Marcelo](https://gist.github.com/mfornet). ### JSON wrapper types To help with serializing certain types to JSON which have unexpected or inefficient default formats, there are some wrapper types in [`near_sdk::json_types`](https://docs.rs/near-sdk/3.1.0/near_sdk/json_types/index.html) that can be used. Because JavaScript only supports integers to value `2^53 - 1`, you will lose precision if deserializing the JSON integer is above this range. To counteract this, you can use the `I64`, `U64`, `I128`, and `U128` in place of the native types for these parameters or result to serialize the value as a string. By default, all integer types will serialize as an integer in JSON. You can convert from `U64` to `u64` and back using `std::convert::Into`, e.g. ```rust #[near] impl Contract { pub fn mult(&self, a: U64, b: U64) -> U128 { let a: u64 = a.into(); let b: u64 = b.into(); let product = u128::from(a) * u128::from(b); product.into() } } ``` You can also access inner values and using `.0`: ```diff #[near] impl Contract { pub fn mult(&self, a: U64, b: U64) -> U128 { - let a: u64 = a.into(); + let a = a.0; - let b: u64 = b.into(); + let b = b.0; let product = u128::from(a) * u128::from(b); product.into() } } ``` And you can cast the lower-case `u` variants to upper-case `U` variants using `U64(...)` and `U128(...)`: ```diff #[near] impl Contract { pub fn mult(&self, a: U64, b: U64) -> U128 { let a = a.0; let b = b.0; let product = u128::from(a) * u128::from(b); - product.into() + U128(product) } } ``` Combining it all: ```rust #[near] impl Contract { pub fn mult(&self, a: U64, b: U64) -> U128 { U128(u128::from(a.0) * u128::from(b.0)) } } ``` Although there are these JSON wrapper types included with the SDK, any custom type can be used, as long as it implements [`serde`](https://serde.rs/) serialize and deserialize respectively. All of these types just override the JSON format and will have a consistent `borsh` serialization and deserialization as the inner types. ### Base64VecU8 Another example of a type you may want to override the default serialization of is `Vec` which represents bytes in Rust. By default, this will serialize as an array of integers, which is not compact and very hard to use. There is a wrapper type [`Base64VecU8`](https://docs.rs/near-sdk/3.1.0/near_sdk/json_types/struct.Base64VecU8.html) which serializes and deserializes to a [Base-64](https://en.wikipedia.org/wiki/Base64) string for more compact JSON serialization. Example here: ```rust #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { // Notice, internally we store `Vec` pub data: Vec, } #[near] impl Contract { #[init] pub fn new(data: Base64VecU8) -> Self { Self { data: data.into(), } } pub fn get_data(self) -> Base64VecU8 { self.data.into() } } ``` --- # Source: https://docs.near.org/smart-contracts/anatomy/serialization.md --- id: serialization title: Notes on Serialization description: "Learn how contract serialize data for function calls and storage." --- Smart contracts need to be able to **communicate complex data** in a simple way, while also **reading and storing** such data into their states efficiently. To achieve such simple communication and efficient storage, smart contracts morph the data from their complex representation into simpler ones. This process of translating **complex objects into simpler single-value** representations is called **serialization**. NEAR uses two serialization formats: [JSON](https://www.json.org/json-en.html) and [Borsh](https://borsh.io/). 1. [JSON](https://www.json.org/json-en.html) is used to serialize the contract's input/output during a function call 2. [Borsh](https://borsh.io/) is used to serialize the contract's state. --- ## Overview of Serialization Formats Let's give a quick overview of both serialization formats, including their pros and cons, as well as an example of what their serializations look like.
### [JSON](https://www.json.org/json-en.html): Objects to Strings #### Features - Self-describing format - Easy interoperability with JavaScript - Multiple implementations readily available - But... it is not efficient both in computational times and resulting size #### Example ```js Example{ number: i32 = 2; arr: Vector = [0, 1]; } // serializes to "{\"number\": 2, \"arr\": [0, 1]}" ```
### [Borsh](https://borsh.io/): Objects to Bytes #### Features - Compact, binary format built to be efficiently (de)serialized - Strict and canonical binary representation - Less overhead: it does not need to store attributes names - But... it is necessary to know the schema to (de)serialize the data #### Example ```js Example{ number: i32 = 2; arr: Vector = [0, 1]; } // serializes into [2, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0] ``` --- ## Serializing Input & Output NEAR contracts can implement methods that both take and return complex objects. In order to handle this data in a simple way, JSON serialization is used. Using JSON makes it easier for everyone to talk with the contracts, since most languages readily implement a JSON (de)serializer. #### Example Let's look at this example, written only for educational purposes: ```rust #[near(serializers = [json])] pub struct A { pub a_number: i32, pub b_number: u128 } #[near(serializers = [json])] pub struct B { pub success: bool, pub other_number: i32 } pub fn method(&self, struct_a: A): B { B { success: true, other_number: 0 } } ``` #### Receiving Data When a user calls the `method`, the contract receives the arguments encoded as a JSON string (e.g. `"{\"a_number\":0, \"b_number\":\"100\"}"`), and proceed to (de)serialize them into the correct object (`A{0, 100}`) . #### Returning Data When returning the result, the contract will automatically encode the object `B{true, 0}` into its JSON serialized value: `"{\"success\":true, \"other_number\":0}"` and return this string. :::caution JSON Limitations Since JSON is limited to `52 bytes` numbers, you cannot use `u64`/`u128` as input or output. JSON simply cannot serialize them. Instead, you must use `Strings`. The `NEAR SDK RS` currently implements the `near_sdk::json_types::{U64, I64, U128, I128}` that you can use for input / output of data. ::: --- ## Borsh: State Serialization Under the hood smart contracts store data using simple **key/value pairs**. This means that the contract needs to translate complex states into simple key-value pairs. For this, NEAR contracts use [borsh](https://borsh.io) which is optimized for (de)serializing complex objects into smaller streams of bytes. :::tip SDK-JS still uses json The JavaScript SDK uses JSON to serialize objects in the state, but the borsh implementation should arrive soon ::: #### Example Let's look at this example, written only for educational purposes: ```rust #[near(serializers = [json, borsh])] #[derive(PanicOnDefault)] pub struct Contract { string: String, vector: Vector } #[near] impl Contract { #[init] pub fn init(string: String, first_u8: u8) -> Self { let mut vector: Vector = Vector::new("prefix".as_bytes()); vector.push(&first_u8); Self { string, vector } } pub fn change_state(&mut self, string: String, number: u8) { self.string = string; self.vector.push(&number); } } ``` #### Empty State On Deploy If we deploy the contract into a new account and immediately ask for the state we will see it is empty: ```bash near view-state $CONTRACT --finality optimistic # Result is: [] ``` #### Initializing the State If we initialize the state we can see how Borsh is used to serialize the state ```bash # initialize with the string "hi" and 0 near call $CONTRACT init '{"string":"hi", "first_u8":0}' --useAccount $CONTRACT # check the state near view-state $CONTRACT --utf8 --finality optimistic ```
Result ```bash [ { key: 'STATE', value: '\x02\x00\x00\x00hi\x01\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00prefix' }, { key: 'prefix\x00\x00\x00\x00\x00\x00\x00\x00', value: '\x00' } ] ```
The first key-value is: ```js key: 'STATE' value: '\x02\x00\x00\x00hi\x01\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00prefix' ``` Since the `Contract` has a structure `string, Vector` the value is interpreted as: ```bash [2, 0, 0, 0, "h", "i"] -> The `string` has 2 elements: "h" and "i". [1, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, "prefix"] -> The Vector has 1 element, and to see the values search for keys that start with (the 6 bytes prefix): "prefix" ``` Then, the second key-value shows the entries of the `Vector` denoted by the `"prefix"` string: ```js key: 'prefix\x00\x00\x00\x00\x00\x00\x00\x00' value: '\x00' ``` #### Modifying the State If we modify the stored string and add a new number, the state changes accordingly: ```bash near call $CONTRACT change_state '{"string":"bye", "number":1}' --useAccount $CONTRACT ```
Result ```bash [ { key: 'STATE', value: '\x03\x00\x00\x00bye\x02\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00prefix' }, { key: 'prefix\x00\x00\x00\x00\x00\x00\x00\x00', value: '\x00' }, { key: 'prefix\x01\x00\x00\x00\x00\x00\x00\x00', value: '\x01' } ] ```
We can see that the `STATE` key changes to reflect the storage of the new string (`bye`), and that the vector now has 2 elements. At the same time, a new key-value was added adding the new vector entry: the `1u8` we just added.
### Deserialization Error When somebody invokes a smart contract method, the first step for the contract is to deserialize its own state. In the example used above, the contract will start by reading the `STATE` key and try to deserialize its value into an object `Contract{string: String, vector: Vector}`. If you deploy a contract into the account with a different Contract structure, then the contract will fail to deserialize the `STATE` key and panic `Cannot deserialize the contract state`. To solve this, you can either: 1. Rollback to the previous contract code 2. Implement a method to [migrate the contract's state](../release/upgrade.md) --- # Source: https://docs.near.org/api/rpc/setup.md --- id: setup title: Setup description: "Learn how to configure NEAR RPC endpoints and test API requests" --- In order to use the RPC API you will need to setup the correct RPC endpoints.
## RPC Endpoint Setup - `POST` for all methods - `JSON RPC 2.0` - `id: "dontcare"` - endpoint URL varies by network: - mainnet `https://rpc.mainnet.near.org` - testnet `https://rpc.testnet.near.org` - betanet `https://rpc.betanet.near.org` _(may be unstable)_ - localnet `http://localhost:3030` ### Limits - Maximum number of requests per IP for mainnet: 30 req/min, 75 req per 10 mins - Maximum number of requests per IP for testnet: 30 req/min, 75 req per 10 mins
## Querying Historical Data Querying historical data (older than 5 [epochs](../../protocol/network/epoch.md) or ~2.5 days), you may get responses that the data is not available anymore. In that case, archival RPC nodes will come to your rescue: - mainnet `https://archival-rpc.mainnet.near.org` - testnet `https://archival-rpc.testnet.near.org` You can see this interface defined in `nearcore` [here](https://github.com/near/nearcore/blob/bf9ae4ce8c680d3408db1935ebd0ca24c4960884/chain/jsonrpc/client/src/lib.rs#L181). ### Limits - Maximum number of requests per IP for mainnet: 4 req/min, 10 req per 10 mins - Maximum number of requests per IP for testnet: 20 req/min, 50 req per 10 mins --- :::important The endpoints https://*.near.org are officially deprecated and heavy rate-limited. Please use any of [the available RPC providers](https://docs.near.org/api/rpc/providers) in production. See [this announcement](https://www.near.org/blog/deprecation-of-near-org-and-pagoda-co-rpc-endpoints) for more info. ::: ## Postman Setup {#postman-setup} An easy way to test the queries in this documentation page is to use an API request tool such as [Postman](https://www.postman.com/). You only need to configure two things: 1. Make sure you add a header with a key of `Content-Type` and value of `application/json`. ![postman-setup-header](/assets/docs/api/postman-setup-headers.png) 2. Then select the `Body` tab and choose the `raw` radio button and ensure `JSON` is the selected format. ![postman-setup-header](/assets/docs/api/postman-setup-body.png) After that is set up, just copy/paste the `JSON object` example snippets below into the `body` of your request, on Postman, and click `send`. --- ## JavaScript Setup {#javascript-setup} All of the queries listed in this documentation page can be called using [`near-api-js`](https://github.com/near/near-api-js). - For `near-api-js` installation and setup please refer to `near-api-js` [quick reference documentation](../../tools/near-api.md). --- ## HTTPie Setup {#httpie-setup} If you prefer to use a command line interface, we have provided RPC examples you can use with [HTTPie](https://httpie.org/). Please note that params take either an object or array passed as a string. ```bash http post https://rpc.testnet.near.org jsonrpc=2.0 id=dontcare method=network_info params:='[]' ``` --- ## Using `block_id` param {#using-block_id-param} The `block_id` param can take either the block number (e.g. `27912554`) or the block hash (e.g. `'3Xz2wM9rigMXzA2c5vgCP8wTgFBaePucgUmVYPkMqhRL'` ) as an argument. :::caution The block IDs of transactions shown in [NearBlocks Explorer](https://testnet.nearblocks.io) are not necessarily the block ID of the executed transaction. Transactions may execute a block or two after its recorded, and in some cases, can take place over several blocks. Due to this, it is important to check subsequent blocks to be sure all results related to the queried transaction are discovered. ::: --- ## Using `finality` param {#using-finality-param} The `finality` param has three options: `optimistic`, `near-final` and `final`: 1. `optimistic` the transaction is in a block that - though unlikely - might be skipped (~1s after submission) 2. `near-final` the transaction is in a block that is irreversible, unless at least one block producer is slashed (~2s after submission) 3. `final` the block is final and irreversible (~3s after submission) The `near-final` finallity has **enough guarantees** for any normal operations, and thus should be preferred for most applications, you can learn more about it [in this official blog post](https://near.org/blog/doomslug-comparison)
How Is Finality Calculated? After a simple transaction (no cross-contract calls) is submitted it is included in a block B and converted into a receipt. At this point, the transaction will be in an "optimistic" state. This means that the transaction could still be skipped, but only if another block is . The receipt for the transaction will execute in the following block (B+1), at which point the transaction is now "near-final". This means that the transaction is now irreversible, unless at least one block producer is slashed (because they produced a malign block). The transaction will be in a "final" state after a new block (B+2) is produced, meaning that it is now irreversible. :::info Cross-Contract Calls For cross-contract calls, the transaction might take longer to be in its final state, as each call to a contract will create a new receipt, and each receipt will be executed in the next block :::
--- # Source: https://docs.near.org/tutorials/multichain-dao/signing.md --- id: signing title: "Abstract Dao: Signatures" description: "Learn how to sign Abstract DAO requests for different chains and relay them to target EVM networks." --- In the previous section, we saw how to register a signature request on the Abstract DAO contract. Now, it is time to sign the transaction for different chains and relay it to the target EVM network. --- ## Signing the Transaction To sign a transaction for a specific chain, the allowed account needs to call the `get_signature` function, passing the `request_id` (generated on the previous section) and all the necessary info to finish creating the transaction before signing it. For example, to sign the transaction for the Sepolia Testnet, the following command can be used: ```bash near contract call-function as-transaction abstract-dao.testnet get_signature json-args '{ "request_id": 1, "other_payload": { "chain_id": 11155111, "max_fee_per_gas": "1000000000", "max_priority_fee_per_gas": "100000000" } }' prepaid-gas '300.0 Tgas' attached-deposit '0.05 NEAR' sign-as executor.testnet network-config testnet ``` Note that all we are specifying now is the `chain_id` (to identify the destination chain), the `max_fee_per_gas`, and the `max_priority_fee_per_gas` (to set the transaction fee). The account authorized to call `get_signature` - in this case `executor.testnet` cannot change any parameter of the transaction being signed besides setting a gas fee per chain. --- ## Signature Response The signature response is going to look like this: ```json { "big_r": { "signature": { "affine_point": "02D532992B0ECBF67800DB14E04530D9BA55609AD31213CC7ABDB554E8FDA986D3" }, "recovery_id": 1, "s": { "scalar": "40E81711B8174712B9F34B2540EE0F642802387D15543CBFC84211BB04B83AC3" } }, "tx": "0x02f85083aa36a702850485034c878517a4eb0789829dd094e2a01146fffc8432497ae49a7a6cba5b9abd71a380a460fe47b1000000000000000000000000000000000000000000000000000000000000a84bc0" } ``` As we can see, it is not the signed transaction itself, but instead the data we need to reconstruct it. We have created an [script](https://github.com/nearuaguild/multichain-dao-scripts) to automate this process, as well as the relaying to the target EVM network. --- # Source: https://docs.near.org/protocol/network/staking.md --- id: staking title: Validator Staking sidebar_label: Staking description: "Learn how to stake NEAR, delegate to validators, track rewards, and withdraw staked tokens safely." --- NEAR uses a Proof-of-Stake (PoS), meaning that users chose the active node validators by delegating their tokens to them. In this article you'll find a detailed explanation of the staking process, including delegating, viewing balances, and withdrawing using the [NEAR CLI](../../tools/cli.md). :::note Contract source code You can review the Staking pool smart contract source code in [this GitHub repository](https://github.com/near/core-contracts/tree/master/staking-pool). ::: --- ## Delegate NEAR tokens Before delegating, you need to choose a validator (a node that participates in staking). Check [NearBlocks](https://nearblocks.io/node-explorer), [Pikespeak](https://pikespeak.ai/validators/overview) or [Near Staking](https://near-staking.com/), and look for validators with a good track record, uptime, and reasonable commission rates.
List validators using CLI If you prefer, you can get the list of current validators by using the [`near-validator`](../../tools/cli.md#validator-extension) CLI: ```sh near-validator validators network-config mainnet now ```

### Stake Tokens ```sh near staking delegation deposit-and-stake '100 NEAR' network-config mainnet sign-with-keychain ``` ```sh near call deposit_and_stake --useAccount --deposit 100 ``` }> :::tip You will start earning staking rewards after the next epoch (approximately 12 hours) :::
### Staked Balance To check your staked balance on the `` pool for the `` account, run the following command: ```sh near staking delegation view-balance network-config mainnet now ``` ```sh near view get_account_staked_balance '{"account_id": ""}' ``` }>
Staking pool balances You can view additional information and balances from the staking pool using the following CLI commands: #### Total staked balance of the entire pool ```sh near view get_total_staked_balance '{}' ``` }> #### Owner of the staking pool ```sh near view get_owner_id '{}' ``` }> #### Current reward fee ```sh near view get_reward_fee_fraction '{}' ``` }> #### Owner's balance ```sh near view get_account_total_balance '{"account_id": "owner"}' ``` }> #### Staking key ```sh near view get_staking_key '{}' ``` }>
--- ## Withdrawing Staked Tokens To withdraw your staked tokens, you will first need to "un-delegate" them from the validator. Your tokens will enter a 4 epoch (~24 hours) unbonding period before they can be withdrawn.
### Unstake Tokens ```sh near staking delegation unstake '1 NEAR' network-config mainnet sign-with-keychain ``` :::info Use the `unstake-all` command to to unstake all tokens at once: ```sh near staking delegation unstake-all network-config mainnet sign-with-keychain ``` ::: ```sh near call unstake '{"amount": "100000000000000000000000000"}' --useAccount ``` :::info Call the `unstake_all` method to unstake all tokens at once ::: }>
### Query Unstaked Balance ```sh near staking delegation view-balance network-config mainnet now ``` ```sh near view get_account_unstaked_balance '{"account_id": ""}' ``` }>
### Withdraw Tokens Once the unbonding period has passed, you can withdraw your unstaked tokens: ```sh near staking delegation withdraw '1 NEAR' network-config mainnet sign-with-keychain ``` :::info If you want to withdraw all available tokens, you can use the `withdraw-all` command: ```sh near staking delegation withdraw-all network-config mainnet sign-with-keychain ``` ::: ```sh near call withdraw '{"amount": "100000000000000000000000000"}' --useAccount ``` }> --- ## Tools and Resources - Supported wallets for staking and managing your tokens: - [Ecosystem Wallets](https://wallet.near.org/) - To explore validators and staking pools: - [NearBlocks](https://nearblocks.io/) - [Pikespeak](https://pikespeak.ai/) - [NEAR Staking](https://near-staking.com/) --- # Source: https://docs.near.org/primitives/nft/standard.md # Source: https://docs.near.org/primitives/linkdrop/standard.md # Source: https://docs.near.org/primitives/ft/standard.md --- id: standard title: The Standard description: "Learn how Fungible Tokens (FT) are defined on NEAR" --- Besides the native NEAR token, users have access to a multitude of tokens created by institutions and other users known as fungible tokens. In contrast with the native token, fungible token (FT) are **not stored** in the user's account, but rather in a smart contract. Such contract is in charge of doing **bookkeeping**, i.e. to track how many tokens each user has, and to handle transfers internally. ![FT](/assets/docs/primitives/ft.png) In order for a contract to be considered a FT contract it has to follow the [**NEP-141**](https://github.com/near/NEPs/tree/master/neps/nep-0141.md) and [**NEP-148 standards**](https://github.com/near/NEPs/tree/master/neps/nep-0148.md)which define the **minimum interface** required to be implemented, as well as the expected functionality. --- ## NEP-141 (Fungible Token Interface) [NEP-141](https://github.com/near/NEPs/tree/master/neps/nep-0141.md) is the blueprint for all fungible tokens (e.g. stablecoins, governance tokens, etc.) on NEAR. It defines a **common set of rules** and **functions** that the contract MUST implement to be considered a fungible token contract. :::tip Notice that the NEP-141 defines the **interface** and **expected behavior** of a fungible token contract, but it does not dictate how the internal logic should be implemented Different FT contracts can have different internal implementations while still adhering to the NEP-141 standard :::
### Interface #### `ft_total_supply` (*read-only*) Returns the total supply of the token ```ts ft_total_supply(): string ```
#### `ft_balance_of` (*read-only*) Returns the balance of a given account ```ts ft_balance_of(account_id: string): string ```
#### `ft_transfer` Transfers `amount` of tokens from the account calling the function to a `receiver_id`, optionally the function can include a `memo` field to provide additional information to the contract > *Requirement: The caller must attach [exactly 1 yoctoNEAR](../../smart-contracts/security/one_yocto.md) to the call* ```ts ft_transfer(receiver_id: string, amount: string, memo: string?): void ```
#### `ft_transfer_call` The function transfers `amount` of tokens to the `receiver_id` **and calls the method `ft_on_transfer(sender_id, amount, msg)`** on `receiver_id`. Optionally the function can include a `memo` for the FT contract, and a `msg` field to which will be sent to the receiver contract. > 📖 This function is useful to transfer tokens to a contract and trigger some action on the receiver side in a single transaction, thus acting as **attaching fungible tokens to a function call**. > *Requirement: The caller must attach [exactly 1 yoctoNEAR](../../smart-contracts/security/one_yocto.md) to the call* ```ts ft_transfer_call(receiver_id: string, amount: string, memo: string?, msg: string): void ```
ft_on_transfer Smart contracts expecting to **receive** Fungible Tokens **must** implement this method. The method **must** return the amount of tokens that were **NOT used** by the receiver, so that the **sender can be refunded**. ```ts ft_on_transfer(sender_id: string, amount: string, msg: string): string ``` ⚠️ Note that this method does not need to be implemented by the FT contract itself, but rather by any contract that expects to receive fungible tokens See a reference implementation in the [using FTs page](./ft.md#handling-deposits)

#### `ft_resolve_transfer` This method is used as a [callback](../../smart-contracts/anatomy/crosscontract.md#callback-function) to resolve the `ft_transfer_call` transaction, handling refunds if necessary. ```js ft_resolve_transfer(sender_id: string, receiver_id: string, amount: string): string ``` --- ## NEP-145 (Storage Management) On NEAR, accounts need to pay for the storage they use on the network. As more users hold tokens on a fungible token contract, more information needs to be stored, and thus the contract needs to reserve more storage. [NEP-145](https://github.com/near/NEPs/blob/master/neps/nep-0145.md) is a standard that defines a common interface for registering users, allowing FT contracts to **charge users for the storage they use**. :::tip While not mandatory, it is highly recommended for FT contracts to implement the NEP-145 standard to avoid running out of storage :::
### Interface #### `storage_balance_bounds` (*read-only*) Returns the minimum and maximum storage balance required for an account to be registered with the contract ```ts storage_balance_bounds(): { min: string, max?: string} | null ``` #### `storage_balance_of` (*read-only*) Returns the storage balance of a given account, or `null` if the account is not registered ```ts storage_balance_of(account_id: string): { total: string, available: string } | null ``` #### `storage_unregister` Removes all information from an account from the contract, returning the storage deposit to the user. The function can only be called by the user themselves. ```ts storage_unregister(force?: boolean): boolean ``` #### `storage_deposit` Registers an account with the contract, reserving enough storage to keep track of the user's balance. The function can be called by the user themselves **or by a third party** on behalf of the user. ```ts storage_deposit(account_id?: string, registration_only?: boolean): { total: string, available: string } ``` #### `storage_withdraw` Unregisters an account from the contract, returning the storage deposit to the user. The function can only be called by the user themselves. ```ts storage_withdraw(amount: string): { total: string, available: string } ``` --- ## NEP-148 (Token Metadata) [NEP-148](https://github.com/near/NEPs/tree/master/neps/nep-0141.md) is an extension to the NEP-141 standard that defines the fungible tokens **metadata**. Metadata provides **key information** about the token, such as its **name, symbol, and decimal precision**, particularly, the following fields MUST be included in the token's metadata: - `spec`: a string. Should be `ft-1.0.0` to indicate that a Fungible Token contract adheres to the current versions of this Metadata and the [Fungible Token Core][FT Core] specs - `name`: the human-readable name of the token - `symbol`: the abbreviation, like wETH or AMPL - `decimals`: used in frontends to show the proper significant digits of a token The metadata is useful for wallets and other user interfaces to display the token correctly, for example if a token is defined as: ```json { "spec": "ft-1.0.0", "name": "My Awesome Token", "symbol": "MAT", "decimals": 4 } ``` A balance of `123456` units of such token should be displayed in a user interface as `12.3456 MAT`. --- # Source: https://docs.near.org/data-infrastructure/tutorials/state-changes.md --- id: state-changes title: "Tutorial: State Changes" description: "This tutorial will guide you through building a simple indexer using the NEAR Lake Framework. The indexer will listen for StateChange events and print relevant data about account changes." --- This tutorial will guide you through building a simple indexer using the NEAR Lake Framework. The indexer will listen for `StateChange` events and print relevant data about account changes. :::note Source code for the tutorial [`near-examples/near-lake-accounts-watcher`](https://github.com/near-examples/near-lake-accounts-watcher/tree/0.2.0): source code for a video tutorial on how to use the NEAR Lake Framework ::: :::info Version 0.2.0 The video is based on the [`near-lake-framework`](/data-infrastructure/near-lake-framework) version 0.2.0 At the same time we're keeping the source code up to date with the latest version of the published crate. ::: We've created a video tutorial to empower the release announcement of [NEAR Lake Framework](/data-infrastructure/near-lake-framework). In this tutorial you will build an indexer application to watch for any `StateChange`s affecting the account or a list of account provided. --- # Source: https://docs.near.org/protocol/storage/storage-solutions.md --- id: storage-solutions title: Decentralized Storage Solutions sidebar_label: Alternative Solutions description: "Explore decentralized storage alternatives for NEAR Protocol applications, including Arweave, Crust, and IPFS integration for cost-effective data storage." --- > In this article you'll find a brief overview of different decentralized storage solutions that can be integrated into your decentralized applications (dApps). This will allow you to store large amounts of data using a more economical alternative to native NEAR storage. - [Arweave](#arweave) - [Crust](#crust) - [IPFS](#ipfs) --- ## On-Chain Storage Constraints For storing data on-chain it's important to keep in mind the following: - You can store an unlimited amount of files, but will cost you 1Ⓝ per 100KB - There is a 4 MB limit on how much you can upload at once For example, if you want to store an NFT purely on-chain (rather than using IPFS or some other decentralized storage solution as mentioned below) you'll have almost an unlimited amount of storage but will have to pay 1 $NEAR per 100 KB of storage used (see [Storage Staking](/protocol/storage/storage-staking)) Users will be limited to 4MB per contract call upload due to `MAX_GAS` constraints. The maximum amount of gas one can attach to a given `functionCall` is 300TGas. ## Arweave [Arweave](https://www.arweave.org/) is a new type of storage that backs data with sustainable and perpetual endowments (tokens held within the protocol that benefit from inflation and the decrease in the cost of storage over long periods of time). This allows users and developers to store data forever. Arweave acts as a collectively owned hard drive, and allows their users to preserve valuable information, apps, and history indefinitely. The Arweave protocol matches a torrent-like swarm of incentivised miners with massive collective hard drive space with those individuals and organizations that need to store data or host content permanently. This is achieved in a decentralized network, and all data stored is backed by block mining rewards and a [sustainable endowment](https://arwiki.wiki/#/en/storage-endowment) ensuring it is available in perpetuity. :::info To learn more about Arweave, check its [mining mechanism](https://arwiki.wiki/#/en/arweave-mining) and its [bandwidth-sharing system](https://arwiki.wiki/#/en/karma). ::: ### Example implementation Let's see how to store some files on Arweave, by running a local Arweave gateway-like server. ### Arlocal setup [Arlocal](https://github.com/textury/arlocal) essentially creates a simulated version of Arweave. Think of it like a local node that runs on your computer to store information. In this example you'll need to run **two terminals**. - Open your first terminal and run: ```bash npx arlocal ``` You should see the response: `arlocal started on port 1984`. :::tip You can specify the port by using `npx arlocal `. ::: ### NEAR-Arweave frontend The [NEAR-Arweave repository](https://github.com/near-examples/NEAR-Arweave-Tutorial) has a simple frontend that allows you to store `.png` files using arlocal. - Now open your second terminal and clone the frontend by running the following command: ```bash git clone https://github.com/near-examples/NEAR-Arweave-Tutorial.git ``` - Install dependencies by running the following in the project folder: ```bash cd NEAR-Arweave-Tutorial yarn ``` - Next, start the application by running: ```bash yarn start ``` - Now you're ready to upload an image by selecting the Choose File button: ![Arweave step 1](/assets/docs/protocol/storage/arweave-1.png) - You should see the transaction ID window become populated after hitting the Submit button: ![Arweave step 2](/assets/docs/protocol/storage/arweave-2.png) :::tip If you get an error, make sure your arlocal node is running in a **separate terminal.** ::: ### Mining your transaction On Arweave your transaction goes through two stages; a pending stage and a confirmed stage. For the transaction to be complete and for you to be able to retrieve your data, your transaction must be confirmed. - Visit `http://localhost:1984/mine` in your browser to send a mine request to your local node. :::tip you may find that you are still able to retrieve your data without this step, but that's because you are running a local node. When dealing with a real Arweave node you will have to wait until your transaction has been mined and confirmed. ::: ### Retrieve the image - Now you can copy and paste any of your listed arweave transaction IDs in step 5 on the frontend to retrieve your file from your local node: ![Arweave step 3](/assets/docs/protocol/storage/arweave-3.png) :::info Using Arweave's live network will require purchasing artokens to pay for storage. You can find out more at [arweave.org](https://www.arweave.org/). ::: :::tip The [near-api-js](https://github.com/near/near-api-js) and [arweave-js](https://github.com/ArweaveTeam/arweave-js) JavaScript libraries allow you to automate most of these steps. ::: --- ## Crust [Crust](https://crust.network) provides a Web3.0 decentralized storage network for the Metaverse. It is designed to realize core values of decentralization, privacy and assurance. Crust supports multiple storage-layer protocols such as IPFS and exposes instant accessible on-chain storage functions to users. Crustʼs technical stack is also capable of supporting data manipulation and computing. The Crust protocol is 100% compatible with the [IPFS](https://ipfs.io) protocol, and it matches people who have hard drive space to spare with those who need to store data or host content. Crust is based on the Polkadot ecosystem and supports most contract platforms, including NEAR/Solana/Ethereum/Elrond/etc. with its cross-chain solution. :::info To learn more about Crust, check its [Decentralized Storage Market](https://wiki.crust.network/docs/en/DSM) and [Guaranteed Proof of Stake](https://wiki.crust.network/docs/en/GPoS). Also, you can start with [Build-101](https://wiki.crust.network/docs/en/build101). ::: ### Integration example Here's a simple integration example to store a file with Crust and NEAR. #### 1. Upload the file to IPFS First, you need to put your files into IPFS. :::tip If you want to learn how to upload **files and folders** into IPFS, please refer to [this section](https://wiki.crust.network/docs/en/buildFileStoringWithGWDemo#1-upload-files-to-ipfs-gateway). ::: There are 2 ways to upload a file to IPFS: - using a local IPFS node - using a remote [IPFS W3Authed Gateway](https://docs.ipfs.io/concepts/ipfs-gateway/#authenticated-gateways) :::info - You can find more details about `ipfsW3GW` endpoints on [this link](https://github.com/crustio/ipfsscan/blob/main/lib/constans.ts#L29). - You can also find a code example on how to upload a file to IPFS on [this link](https://github.com/crustio/crust-demo/blob/main/near/src/index.ts#L20-L51). ::: #### 2. Place an storage order Next, you need to send a transaction named `Place Storage Order` on Crust chain. This transaction will dispatch your storage requirement to each Crust IPFS nodes through the blockchain. Then the IPFS nodes will start pulling your file using the IPFS protocol. :::info - You can find more information about `crustChainEndpoint` on [this link](https://github.com/crustio/crust-apps/blob/master/packages/apps-config/src/endpoints/production.ts#L9). - You can create your own account (`seeds`) following [these instructions](https://wiki.crust.network/docs/en/crustAccount#create-an-account-1). - Check [this link](https://github.com/crustio/crust-demo/blob/main/near/src/index.ts#L82-L112) for a code example about placing a storage order on Crust. ::: #### 3. Query order status Then, you can query the storage order by calling on-chain status (`status{replica_count, storage_duration, ...}`). This call will return: ```json { "file_size": 23710, "spower": 24895, "expired_at": 2594488, // Storage duration "calculated_at": 2488, "amount": "545.3730 nCRU", "prepaid": 0, "reported_replica_count": 1, // Replica count "replicas": [{ "who": "cTHATJrSgZM2haKfn5e47NSP5Y5sqSCCToxrShtVifD2Nfxv5", "valid_at": 2140, "anchor": "0xd9aa29dda8ade9718b38681adaf6f84126531246b40a56c02eff8950bb9a78b7c459721ce976c5c0c9cd4c743cae107e25adc3a85ed7f401c8dde509d96dcba0", "is_reported": true, "created_at": 2140 }] // Who stores the file } ``` :::info Find a code example about querying storage status on [this link](https://github.com/crustio/crust-demo/blob/main/near/src/index.ts#L144-L147). ::: #### 4. Add file prepaid The default storage time for a single transaction (order) is 6 months. If you want to extend the storage duration, Crust provides a prepaid pool so you can customize the file's storage time. This pool allows you to put some tokens and will automatically extend the file's storage time. :::info Follow [this link](https://github.com/crustio/crust-demo/blob/main/near/src/index.ts#L114-L142) for a code snippet on how to add prepaid tokens to your files. ::: --- ## IPFS The [InterPlanetary File System](https://ipfs.io/) (IPFS) is a protocol and peer-to-peer network for storing and sharing data in a distributed file system. IPFS uses content-addressing to uniquely identify each file in a global namespace connecting all computing devices. ### Content identifier When you add a file to IPFS it is split into cryptographically hashed smaller chunks and then given a unique fingerprint called a content identifier (CID). :::tip The CID acts as an permanent record of a file as it exists at that point in time. ::: ### Look-up When a node looks up for a file, it ask the peer nodes for the content referenced by the file's CID. When a node views or downloads a file, it caches a copy and become another provider until the cache is cleared. ### Pinned content On the IPFS network, each node stores only content it is interested in. A node can pin content in order to keep it forever, or discard content it hasn't used to save space. ### File versions When you add a new version of your file to IPFS it will get a new CID since the cryptographic hash is different. This means that any changes to a file will not overwrite the original and common chunks across files can be reused in order to minimize storage costs. ### Naming system IPFS offers a decentralized naming system so you don't need to remember a long string of CIDs. IPFS can find the latest version of your file using the IPNS decentralized naming system and you can use DNSLink to map CIDs to human-readable DNS names. ### IPFS providers - [Web3.Storage](https://web3.storage/): it's a service that simplifies building on top of IPFS and Filecoin. Web3.Storage is backed by Filecoin and makes content available via IPFS, leveraging the unique properties of each network. - [NFT.Storage](https://nft.storage/): this service is built specifically for storing off-chain NFT data. Data is stored decentralized on IPFS and Filecoin. The data is referenced using content-addressed IPFS URIs that can be used in your smart contracts. - [Filebase](https://filebase.com/): a geo-redundant IPFS pinning provider that pins all IPFS files with automatic 3x redundancy across diverse, geographic locations for additional performance, redundancy, and reliability. --- # Source: https://docs.near.org/protocol/storage/storage-staking.md --- id: storage-staking title: Storage Staking sidebar_label: Storage Staking description: "Learn about NEAR Protocol's storage staking mechanism, including costs, storage pricing, attack prevention, and strategies for managing on-chain data storage." --- NEAR's storage staking is a mechanism that ensures the network remains efficient and secure by requiring smart contract owners to stake tokens based on the amount of data their contracts store. This helps maintain the integrity of the network while providing a predictable cost structure for developers. > When you deploy a smart contract to NEAR, you pay for the storage that this contract requires using a mechanism called storage staking. > > In storage staking (sometimes called _state_ staking), the account that owns a smart contract must stake (or lock) tokens according to the amount of data stored in that smart contract, effectively reducing the balance of the contract's account. ## How does NEAR's design align incentives? Storage-staked tokens are unavailable for other uses, such as validation staking. This increases the yield that validators will receive. Learn more in [the economics whitepaper](https://pages.near.org/papers/economics-in-sharded-blockchain/). ## When do tokens get staked? On each incoming transaction that adds data. Let's walk through an example: 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. ## The "million cheap data additions" attack Note that this can create an attack surface. To continue the example above, if sending data to your guest book costs users close to nothing while costing the contract owner significantly more, then a malicious user can exploit the imbalance to make maintaining the contract prohibitively expensive. Take care, then, when designing your smart contracts to ensure that such attacks cost potential attackers more than it would be worth. ## btw, you can remove data to unstake some tokens People familiar with the "immutable data" narrative about blockchains find this surprising. While it's true that an _indexing node_ will keep all data forever, _validating nodes_ (that is, the nodes run by most validators in the network) do not. Smart contracts can provide ways to delete data, and this data will be purged from most nodes in the network within a few [epochs](../network/epoch.md). Note that a call to your smart contract to remove data has an associated gas fee. Given NEAR's gas limit, this creates an upper limit on how much data can be deleted in a single transaction. ## How much does it cost? Storage staking is priced in an amount set by the network, which is set to **1E19 yoctoNEAR per byte**, or **100kb per NEAR token (Ⓝ)**. [^1] [^2] NEAR's JSON RPC API provides [a way to query this initial setting](/api/rpc/protocol#genesis-config) as well as a [a way to query the live config / recent blocks](/api/rpc/protocol#protocol-config). ## Example cost breakdown Let's walk through an example. A [non-fungible token](https://github.com/near/NEPs/pull/4) is unique, which means each token has its own ID. The contract must store a mapping from token IDs to owners' account ID. If such an NFT is used to track **1 million** tokens, how much storage will be required for the token-ID-to-owner mapping? And how many tokens will need to be staked for that storage? Let's calculate the storage needs when using a `PersistentMap` that stores data as UTF-8 strings. Here's our `PersistentMap`: ```ts type AccountId = string; type TokenId = u64; const tokenToOwner = new PersistentMap("t2o"); ``` Behind the scenes, all data stored on the NEAR blockchain is saved in a key-value database. That `'t2o'` variable that's passed to `PersistentMap` helps it keep track of all its values. If your account `example.near` owns token with ID `0`, then at the time of writing, here's the data that would get saved to the key-value database: - key: `t2o::0` - value: `example.near` So for 1 million tokens, here are all the things we need to add up and multiply by 1 million: 1. The prefix, `t2o`, will be serialized as three bytes in UTF-8, and the two colons will add another two. That's 5 bytes. 2. For an implementation where `TokenId` auto-increments, the values will be between `0` and `999999`, which makes the average length 5 bytes. 3. Let's assume well-formed NEAR `AccountId`s, and let's guess that NEAR Account IDs follow the approximate pattern of domain names, which [average about 10 characters](https://www.gaebler.com/Domain-Length-Research.htm), plus a top-level name like `.near`. So a reasonable average to expect might be about 15 characters; let's keep our estimate pessimistic and say 25. This will equal 25 bytes, since NEAR account IDs must use characters from the ASCII set. So: 1_000_000 * (5 + 5 + 25) 35 million bytes. 350 times 100Kib, meaning Ⓝ350. To do the exact math: Multiplying by 1e19 yoctoNEAR per byte, we find that the `tokenToOwner` mapping with 35m bytes will require staking 3.5e26 yoctoNEAR, or Ⓝ350 Note that you can get this down to Ⓝ330 just by changing the prefix from `t2o` to a single character. Or get rid of it entirely! You can have a zero-length prefix on one `PersistentVector` in your smart contract. If you did that with this one, you could get it down to Ⓝ250. ## Calculate costs for your own contract Doing manual byte math as shown above is difficult and error-prone. Good news: you don't have to! You can test the storage used using the [SDK environment](../../smart-contracts/anatomy/environment.md) and checking `env.storage_usage()` ## Other ways to keep costs down Storing data on-chain isn't cheap for the people running the network, and NEAR passes on this cost to developers. So, how do you, as a developer, keep your costs down? There are two popular approaches: 1. Use a binary serialization format, rather than JSON 2. Store data off-chain ### Use a binary serialization format, rather than JSON The core NEAR team maintains a library called [borsh](https://borsh.io/), which is used automatically when you use `near-sdk-rs`. Someday, it will probably also be used by `near-sdk-js`. Imagine that you want to store an array like `[0, 1, 2, 3]`. You could serialize it as a string and store it as UTF-8 bytes. This is what `near-sdk-js` does today. Cutting out spaces, you end up using 9 bytes. Using borsh, this same array gets saved as 8 bytes: \u0004\u0000\u0000\u0000\u0000\u0001\u0002\u0003 At first glance, saving 1 byte might not seem significant. But let's look closer. The first four bytes here, `\u0004\u0000\u0000\u0000`, tell the serializer that this is a `u32` array of length `4` using little-endian encoding. The rest of the bytes are the literal numbers of the array – `\u0000\u0001\u0002\u0003`. As you serialize more elements, each will add one byte to the data structure. With JSON, each new element requires adding two bytes, to represent both another comma and the number. In general, Borsh is faster, uses less storage, and costs less gas. Use it if you can. ### Store data off-chain This is especially important if you are storing user-generated data! Let's use this [Guest Book](https://github.com/near-examples/guest-book-examples) as an example. As implemented today, visitors to the app can sign in with NEAR and leave a message. Their message is stored on-chain. Imagine this app got very popular, and that visitors started leaving unexpectedly long messages. The contract owner might run out of funding for storage very quickly! A better strategy could be to store data off-chain. If you want to keep the app decentralized, a popular off-chain data storage solution is [IPFS](https://ipfs.io/). With this, you can represent any set of data with a predictable content address such as: QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG Such a content address could represent a JSON structure or an image or any other type of data. Where does this data get physically stored? You could use [Filecoin](https://filecoin.io/) or run your own IPFS server to pin your app's data. With this approach, each record you add to your contract will be a predictable size. ## Summary NEAR's structure incentivizes network operators while giving flexibility and predictability to contract developers. Managing storage is an important aspect of smart contract design, and NEAR's libraries make it easy to figure out how much storage will cost for your application. :::tip Got a question? Ask it on StackOverflow! ::: ## Footnotes [^1]: [Storage staking price](https://gov.near.org/t/storage-staking-price/399) [^2]: [Lower storage cost 10x](https://github.com/near/nearcore/pull/3881) --- # Source: https://docs.near.org/smart-contracts/security/storage.md # Source: https://docs.near.org/smart-contracts/anatomy/storage.md --- id: storage title: State sidebar_label: State (Storage) hide_table_of_contents: true description: "Explore how NEAR smart contracts manage their state." --- Smart contracts store data in their account's state, which is public on the chain. The storage starts **empty** until a contract is deployed and the state is initialized. It is important to know that the account's **code** and account's **storage** are **independent**. [Updating the code](../release/upgrade.md) does **not erase** the state.
### Defining the State The attributes of the `class` marked as the contract define the data that will be stored. The contract can store all native types (e.g. `number`, `string`, `Array`, `Map`) as well as complex objects. For example, our Auction contract stores when the auction ends, and an object representing the highest bid. **Note:** The SDK also provides [collections](./collections.md) to efficiently store collections of data. ### Defining the State The attributes of the `struct` marked as the contract define the data that will be stored. The contract can store all native types (e.g. `u8`, `string`, `HashMap`, `Vector`) as well as complex objects. For example, our Auction contract stores when the auction ends, and an object representing the highest bid. **Note:** The structures that will be saved need a special macro, that tells the SDK to store them [serialized in Borsh](./serialization.md). **Note:** The SDK also provides [collections](./collections.md) to efficiently store collections of data. :::warning Contracts pay for their storage by locking part of their balance. It currently costs ~**1Ⓝ** to store **100KB** of data. ::: ### Initializing the State After the contract is deployed, its state is empty and needs to be initialized with some initial values. There are two ways to initialize a state: 1. By creating an initialization function 2. By setting default values ### I. Initialization Functions An option to initialize the state is to create an `initialization` function, which needs to be called before executing any other function. In our Auction example, the contract has an initialization function that sets when the auction ends. Note the `@initialization` decorator, and the forced initialization on `NearBindgen`. **Note:** It is a good practice to mark initialization functions as private. We will cover function types in the [functions section](./functions.md). :::warning In Python, you need to manage the state initialization explicitly. The SDK doesn't enforce that initialization happens before other methods are called - you'll need to add your own checks if required. ::: ### I. Initialization Functions An option to initialize the state is to create an `initialization` function, which needs to be called before executing any other function. In our Auction example, the contract has an initialization function that sets when the auction ends. The contract derives the `PanicOnDefault`, which forces the user to call the init method denoted by the `#[init]` macro. **Note:** It is a good practice to mark initialization functions as private. We will cover function types in the [functions section](./functions.md). ### II. Default State Another option to initialize the state is to set default values for the attributes of the class. Such is the case for our "Hello World" contract, which stores a `greeting` with the default value `"Hello"`. The first time the contract is called (somebody executes `get_greeting` or `set_greeting`), the default values will be stored in the state, and the state will be considered initialized. **Note:** The state can only be initialized once. ### II. Default State Another option to initialize the state is to create a `Default` version of our contract's `struct`. For example, our "Hello World" contract has a default state with a `greeting` set to `"Hello"`. The first time the contract executes, the `Default` will be stored in the state, and the state will be considered initialized. **Note:** The state can only be initialized once. ### Lifecycle of the State When a function is called, the contract's state is loaded from the storage and put into memory. The state is actually [stored serialized](./serialization.md), and the SDK takes a bit of time to deserialize it before the method can access it. When the method finishes executing successfully, all the changes to the state are serialized, and saved back to the storage. :::warning State and Code In NEAR, the contract's code and contract's storage are **independent**. Updating the code of a contract does **not erase** the state, and can indeed lead to unexpected behavior or errors. Make sure to read the [updating a contract](../release/upgrade.md) if you run into issues. ::: ```js import { NearBindgen, near, call, view, AccountId, NearPromise, initialize, assert } from "near-sdk-js"; class Bid { bidder: AccountId; bid: bigint; } @NearBindgen({ requireInit: true }) class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(0) }; auction_end_time: bigint = BigInt(0); auctioneer: AccountId = ""; claimed: boolean = false; @initialize({ privateFunction: true }) init({ end_time, auctioneer}: { end_time: bigint, auctioneer: AccountId}) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; this.auctioneer = auctioneer; } @call({ payableFunction: true }) bid(): NearPromise { // Assert the auction is still ongoing assert(this.auction_end_time > near.blockTimestamp(), "Auction has ended"); // Current bid const bid = near.attachedDeposit(); const bidder = near.predecessorAccountId(); // Last bid const { bidder: lastBidder, bid: lastBid } = this.highest_bid; // Check if the deposit is higher than the current bid assert(bid > lastBid, "You must place a higher bid"); // Update the highest bid this.highest_bid = { bidder, bid }; // Save the new bid // Transfer tokens back to the last bidder return NearPromise.new(lastBidder).transfer(lastBid); } @call({}) claim() { assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); assert(!this.claimed, "Auction has been claimed"); this.claimed = true; return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) } @view({}) get_highest_bid(): Bid { return this.highest_bid; } @view({}) get_auction_end_time(): BigInt { return this.auction_end_time; } ``` ```js import { NearBindgen, near, call, view } from 'near-sdk-js'; @NearBindgen({}) class HelloNear { greeting: string = 'Hello'; static schema = { // JS contracts need a schema "greeting": "string" }; @view({}) // This method is read-only and can be called for free get_greeting(): string { return this.greeting; } @call({}) // This method changes the state, for which it cost gas set_greeting({ greeting }: { greeting: string }): void { near.log(`Saving greeting ${greeting}`); ``` ```rust use near_sdk::json_types::U64; use near_sdk::{env, near, require, AccountId, NearToken, PanicOnDefault, Promise}; #[near(serializers = [json, borsh])] #[derive(Clone)] pub struct Bid { pub bidder: AccountId, pub bid: NearToken, } #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { highest_bid: Bid, auction_end_time: U64, auctioneer: AccountId, claimed: bool, } #[near] impl Contract { #[init] #[private] // only callable by the contract's account pub fn init(end_time: U64, auctioneer: AccountId) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: NearToken::from_yoctonear(1), }, auction_end_time: end_time, claimed: false, auctioneer, } } #[payable] pub fn bid(&mut self) -> Promise { // Assert the auction is still ongoing require!( env::block_timestamp() < self.auction_end_time.into(), "Auction has ended" ); // Current bid let bid = env::attached_deposit(); let bidder = env::predecessor_account_id(); // Last bid let Bid { bidder: last_bidder, bid: last_bid, } = self.highest_bid.clone(); // Check if the deposit is higher than the current bid require!(bid > last_bid, "You must place a higher bid"); // Update the highest bid self.highest_bid = Bid { bidder, bid }; // Transfer tokens back to the last bidder Promise::new(last_bidder).transfer(last_bid) } pub fn claim(&mut self) -> Promise { require!( env::block_timestamp() > self.auction_end_time.into(), "Auction has not ended yet" ); require!(!self.claimed, "Auction has already been claimed"); self.claimed = true; // Transfer tokens to the auctioneer Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid) } pub fn get_highest_bid(&self) -> Bid { self.highest_bid.clone() } pub fn get_auction_end_time(&self) -> U64 { self.auction_end_time } ``` ```rust use near_sdk::{log, near}; // Define the contract structure #[near(contract_state)] pub struct Contract { greeting: String, } // Define the default, which automatically initializes the contract impl Default for Contract { fn default() -> Self { Self { greeting: "Hello".to_string(), } } } // Implement the contract structure #[near] impl Contract { // Public method - returns the greeting saved, defaulting to DEFAULT_GREETING pub fn get_greeting(&self) -> String { self.greeting.clone() } // Public method - accepts a greeting, such as "howdy", and records it pub fn set_greeting(&mut self, greeting: String) { log!("Saving greeting: {}", greeting); self.greeting = greeting; } } ``` ```python class AuctionContract(Contract): @init def initialize(self, end_time, auctioneer): """Initialize the auction contract with an end time and auctioneer account""" # Save auction end time self.storage["auction_end_time"] = end_time # Initialize highest bid with current account and 1 yocto initial_bid = { "bidder": self.current_account_id, "bid": ONE_YOCTO } self.storage["highest_bid"] = initial_bid # Set auctioneer self.storage["auctioneer"] = auctioneer # Initialize claimed status self.storage["claimed"] = False return {"success": True} @call def bid(self): """Place a bid in the auction""" # Get auction end time auction_end_time = self.storage["auction_end_time"] # Assert the auction is still ongoing current_time = self.block_timestamp if current_time >= auction_end_time: raise InvalidInput("Auction has ended") # Current bid bid_amount = self.attached_deposit bidder = self.predecessor_account_id # Get last bid highest_bid = self.storage["highest_bid"] last_bidder = highest_bid["bidder"] last_bid = highest_bid["bid"] # Check if the deposit is higher than the current bid if bid_amount <= last_bid: raise InvalidInput("You must place a higher bid") # Update the highest bid new_highest_bid = { "bidder": bidder, "bid": bid_amount } self.storage["highest_bid"] = new_highest_bid # Log the new bid self.log_event("new_bid", { "bidder": bidder, "amount": bid_amount }) # Transfer tokens back to the last bidder return CrossContract(last_bidder).transfer(last_bid).value() @call def claim(self): """Claim the auction proceeds after it has ended""" # Get auction end time and claimed status auction_end_time = self.storage["auction_end_time"] claimed = self.storage.get("claimed", False) # Assert the auction has ended current_time = self.block_timestamp if current_time <= auction_end_time: raise InvalidInput("Auction has not ended yet") # Assert the auction has not been claimed if claimed: raise InvalidInput("Auction has already been claimed") # Mark as claimed self.storage["claimed"] = True # Get highest bid and auctioneer highest_bid = self.storage["highest_bid"] auctioneer = self.storage["auctioneer"] # Log the claim self.log_event("auction_claimed", { "winner": highest_bid["bidder"], "amount": highest_bid["bid"] }) # Transfer tokens to the auctioneer return CrossContract(auctioneer).transfer(highest_bid["bid"]).value() @view def get_highest_bid(self): """Returns the current highest bid information""" return self.storage.get("highest_bid") @view def get_auction_end_time(self): """Returns the auction end time""" return self.storage.get("auction_end_time") @view def get_auction_status(self): """Returns the overall status of the auction""" current_time = self.block_timestamp auction_end_time = self.storage["auction_end_time"] highest_bid = self.storage["highest_bid"] claimed = self.storage.get("claimed", False) return { "is_active": current_time < auction_end_time, "time_remaining": max(0, auction_end_time - current_time), "highest_bidder": highest_bid["bidder"], "highest_bid": highest_bid["bid"], "claimed": claimed, "auctioneer": self.storage["auctioneer"] } ``` ```python class HelloNear: @init def new(self): # Initialize state with default greeting self.storage["greeting"] = "Hello" @view def get_greeting(self) -> str: """Returns the current greeting""" return self.storage["greeting"] @call def set_greeting(self, message: str) -> str: """Sets a new greeting""" self.storage["greeting"] = message return message ``` --- # Source: https://docs.near.org/smart-contracts/security/sybil.md --- id: sybil title: Sybil Attacks description: "Learn about sybil attacks in NEAR smart contracts and how to prevent them with proper identity verification and anti-gaming mechanisms." --- While developing your smart contract, keep in mind that an individual can potentially create multiple NEAR accounts. This is especially relevant in ecosystems involving crowd decisions, such as [DAOs](../../primitives/dao.md). Imagine that you open the voting to anyone in the community. If each account can cast a vote, a malicious actor could span multiple accounts and gain a disproportionately large influence on the result. --- # Source: https://docs.near.org/integrations/errors/token-loss.md # Source: https://docs.near.org/protocol/network/token-loss.md --- id: token-loss title: Avoiding Token Loss sidebar_label: Avoiding Token Loss description: "Learn about scenarios that can lead to token loss in NEAR Protocol and how to prevent them, including key management, account deletion, and smart contract failures." --- This document outlines the common errors that can lead to token loss and how to avoid them. :::warning Careful! Losing tokens means losing money! ::: Token loss is possible under multiple scenarios. These scenarios can be grouped into a few related classes: 1. Improper key management 2. Refunding deleted accounts 3. Failed function calls in batches --- ## Improper key management Improper key management may lead to token loss. Mitigating such scenarios may be done by issuing backup keys allowing for recovery of accounts whose keys have been lost or deleted. ### Loss of `FullAccess` key A user may lose their private key of a `FullAccess` key pair for an account with no other keys. No one will be able to recover the funds. Funds will remain locked in the account forever. ### Loss of `FunctionCall` access key An account may have its one and only `FunctionCall` access key deleted. No one will be able to recover the funds. Funds will remain locked in the account forever. --- ## Refunding deleted accounts When a refund receipt is issued for an account, if that account no longer exists, the funds will be dispersed among validators proportional to their stake in the current epoch. ### Deleting account with non-existent beneficiary When you delete an account, you must assign a beneficiary. Once deleted, a transfer receipt is generated and sent to the beneficiary account. If the beneficiary account does not exist, a refund receipt will be generated and sent back to the original account. Since the original account has already been deleted, the funds will be dispersed among validators. ### Account with zero balance is garbage-collected, just before it receives refund If an account `A` transfers all of its funds to another account `B` and account `B` does not exist, a refund receipt will be generated for account `A`. During the period of this round trip, account `A` is vulnerable to deletion by garbage collection activities on the network. If account `A` is deleted before the refund receipt arrives, the funds will be dispersed among validators. --- ## Failed function calls in batches :::warning When designing a smart contract, you should always consider the asynchronous nature of NEAR Protocol. ::: If a contract function `f1` calls two (or more) other functions `f2` and `f3`, and at least one of these functions, `f2` and `f3` fails, then tokens will be refunded from the function that failed, but tokens will be appropriately credited to the function(s) which succeed. The successful call's tokens may be considered lost depending on your use case if a single failure in the batch means the whole batch failed. --- # Source: https://docs.near.org/protocol/data-flow/token-transfer-flow.md --- sidebar_label: Token Transfer description: "Learn all steps involved on a token transfer." --- This section explains the flow of token transfers in the NEAR Protocol, detailing how tokens are moved between accounts, the role of receipts, and the importance of gas refunds in the process. # Token Transfer flow In the [previous article](near-data-flow.md) we saw an example of token transfer between accounts found in different shards. That example was simplified and missed a few steps in the process. That was intentional, to keep the article and the video short yet explanatory, in order to give you a bigger picture for understanding concepts. In this article we're going to have a look at the same data flow, but in detail and we will consider two additional scenarios: - Token transfer between accounts found in different shards - Token transfer between accounts found in the same shard You may be asking what was missing in the previous explanation. The short answer is: **Gas Refunds** or simply **Refunds**. If you don't know what **Gas** is, please [first read the article about Gas](/protocol/gas) from our docs. As for *Refunds*, here's a quote from the [Gas](/protocol/gas) article: > Attach extra gas; get refunded! > > ... > > - If you attach more gas than needed, you'll get refunded > > ... > > *From NEAR Protocol docs [Gas. Attach extra gas; get refunded!](/protocol/gas)* :::note What refunds mean in terms of data flow It means that literally every Transaction includes a refund. ::: OK, this should be enough for introduction, now let's move on to examples. ## Token transfer between accounts found in different shards Basically it is an extension of the example from the [NEAR Data Flow](near-data-flow.md) article. Assume we have two accounts **alice.near** and **bob.near**. They belong to different `Shards`. **alice.near** sends a few tokens to **bob.near**. A `Transaction` signed by **alice.near** is sent to the network. It is immediately executed, `ExecutionOutcome` is the output or result from converting the transaction into a `Receipt`. ![Transaction execution](/assets/docs/protocol/data-flow/03-tx-outcome-receipt.png) During the above process **alice.near**, the sender, was charged a fee (gas). The `Receipt` created as result of the `Transaction` follows these rules: 1. It will be executed not earlier than next `Block` 2. It **must** be executed on the receiver's `Shard` So, in our case the receiver is **bob.near** and that account belongs to a different `Shard` that's why the `Receipt` moves to the receiver's Shard and is put in the execution queue. In our example the Receipt is executed in the very next Block. ![The Receipt is executed in the next Block](/assets/docs/protocol/data-flow/04-send-nears-flow.png) Almost done. Remember the refund? So the `ExecutionOutcome` for the Receipt will be another Receipt that is refunding the Gas to the sender. **bob.near** has received tokens from **alice.near**. Now, **alice.near** becomes the receiver for a new (and last) Receipt (keep in mind the sender in this Receipt is always **system**). Keep in mind rule #2: the Receipt must be executed on the receiver's Shard. So this Receipt moves to the Shard where **alice.near** belongs to. And it is the last execution in this process. ![Complete scheme of Token transfer between the accounts from different Shards](/assets/docs/protocol/flow-token-transfer/01-diff-shards-complete.png) This is it. Tokens have been transferred from the account on one Shard to the account on a different Shard, and the initial sender, **alice.near**, received a refund of Gas. ## Token transfer between accounts found on the same shard Let's have a look at the example where both accounts are on the same `Shard`. The process is the same as in the previous example, except there are no Receipts moving from one Shard to another. A `Transaction` signed by **alice.near** is sent to the network. It is immediately executed, `ExecutionOutcome` is the result of converting the transaction into a `Receipt`. ![Transaction execution](/assets/docs/protocol/data-flow/03-tx-outcome-receipt.png) The Receipt is already on the receiver's Shard, so it is put in the execution queue of the next `Block`. It is executed in the next Block, and the `ExecutionOutcome` result is a new Receipt with the refund to the initial sender, **alice.near**. The Same rules apply to this Receipt, it is put into the execution queue and executed in the next Block. ![Complete scheme of Token transfer between the account from the same Shards](/assets/docs/protocol/flow-token-transfer/02-same-shard-complete.png) This is it. You may wonder why the process is overcomplicated for the same Shard case. The answer is: **the same rules are always applied**. Also, this mechanism allows to build the NEAR Protocol data flow by only one set of rules, no matter how many Shards exist. Also, we avoid a lot of "ifs" and we don't have to keep in mind different corner cases because the process always follows the same rules. --- # Source: https://docs.near.org/protocol/network/tokens.md --- id: tokens title: Tokens description: "Learn about NEAR's native token and its role in the network" --- NEAR Protocol has a native token, NEAR (Ⓝ), which is used for various purposes within the ecosystem. This document provides an overview of the NEAR token, its use cases, and how it functions within the NEAR Protocol. # NEAR Token This is the native token used in NEAR Protocol. It has multiple use cases: - Secures the network through staking - Provides a unit of account - NEAR is used for processing transactions and storing data - Serves as a medium of exchange ### Securing the Network NEAR Protocol is a proof-of-stake (PoS) network, which means that resistance from various attacks comes from staking NEAR. Staked NEAR represents the decentralized infrastructure of servers that maintain the network and process transactions for applications and users on NEAR. Rewards for providing this service are received in NEAR. ## Providing a Unit of Account NEAR is used to price computation and storage on the NEAR infrastructure. The network charges transaction fees in NEAR to process changes and transactions. ## Medium of Exchange NEAR is readily available on the protocol level, so it can be used to transfer value between NEAR applications and accounts. This means that applications can use NEAR to charge for various functions, like access to data or other complex transactions. Entities can also easily exchange NEAR between each other, without the need for trusted third parties to clear and settle transactions. For a deeper dive on NEAR Economics: [https://near.org/blog/near-protocol-economics](https://near.org/blog/near-protocol-economics) For more information about the NEAR token, visit [NEAR Token Supply and Distribution](https://near.org/blog/near-token-supply-and-distribution/) or [Nomicon](https://nomicon.io). --- # Source: https://docs.near.org/protocol/transaction-anatomy.md --- id: transaction-anatomy title: Anatomy of a Transaction description: "Learn about the structure and components of NEAR Protocol transactions, including signers, receivers, actions, and transaction validation fields." --- This section explains the anatomy of a transaction in the NEAR Protocol, describing its structure, components, and the actions that can be performed within a transaction. A transaction is a request from a user to the network to perform a set of actions. To create a transaction, the user must specify the following fields: - `Signer`: the account that signs the transaction - `Actions`: the set of actions to be performed (see below) - `Receiver`: the account on which to perform the actions In addition, a transaction has the following fields to ensure its integrity and validity: - `PublicKey`: the public key used to sign the transaction (so the network can verify the signature) - `Nonce`: a number that is incremented for each transaction sent by the `Signer` - `BlockHash`: the hash of a recent block, to limit the time-validity of the transaction Users create transactions and sign them with their private keys. Then, the **transaction and its signature** are broadcast together to the network, where they are validated and processed. :::tip Each transaction has exactly one `Signer` and `Receiver`, but can have multiple `Actions`. ::: :::info About nonce values - When adding a key, the `nonce` is automatically assigned - particularly, it is given the value `block height * 10^6` - so the value in the `ADD_KEY` action is ignored - A transaction is accepted only if the `nonce` value is in the range of: - `[(current_nonce_of_access_key + 1) .. (block_height * 10^6)]` - Once a transaction is accepted, the access key's `nonce` is set to the `nonce` value of the included transaction ::: --- ## Actions Each transaction can have **one or multiple** [`Actions`](https://nomicon.io/RuntimeSpec/Actions.html), which are the actual operations to be performed on the `Receiver` account. There are different types of actions that can be performed: 1. [`FunctionCall`](https://nomicon.io/RuntimeSpec/Actions.html#functioncallaction): to invoke a function on a contract (optionally attaching NEAR to the call) 2. [`Transfer`](https://nomicon.io/RuntimeSpec/Actions.html#transferaction): to transfer tokens to another account 3. [`DeployContract`](https://nomicon.io/RuntimeSpec/Actions.html#deploycontractaction): to deploy a contract in the account 4. [`DeployGlobalContract`](https://nomicon.io/RuntimeSpec/Actions.html#deployglobalcontractaction): registers a global contract referenced by its contract hash or by its account ID 5. [`UseGlobalContract`](https://nomicon.io/RuntimeSpec/Actions.html#useglobalcontractaction): uses a registered global contract by contract hash or account ID 6. [`CreateAccount`](https://nomicon.io/RuntimeSpec/Actions.html#createaccountaction): to create a new sub-account (e.g. `ana.near` can create `sub.ana.near`) 7. [`DeleteAccount`](https://nomicon.io/RuntimeSpec/Actions.html#deleteaccountaction): to delete the account (transferring the remaining balance to a beneficiary) 8. [`AddKey`](https://nomicon.io/RuntimeSpec/Actions.html#addkeyaction): to add a new key to the account (either `FullAccess` or `FunctionCall` access) 9. [`DeleteKey`](https://nomicon.io/RuntimeSpec/Actions.html#deletekeyaction): to delete an existing key from the account 10. [`DelegateActions`](https://nomicon.io/RuntimeSpec/Actions.html#delegate-actions): to create a meta-transaction 11. [`Stake`](https://nomicon.io/RuntimeSpec/Actions.html#stakeaction): special action to express interest in becoming a network validator 12. [`DeterministicStateInit`](https://nomicon.io/RuntimeSpec/Actions.html#deterministicstateinitaction): special action to create a deterministic account (an advanced kind of implicit account) For example, `bob.near` can bundle the following actions in a single transaction: - Create the account `contract.bob.near` - Transfer 5 NEAR to `contract.bob.near` - Deploy a contract in `contract.bob.near` - Call the function `init` in `contract.bob.near` The `Actions` are executed in the **order they are specified in the transaction**. If any of the **actions fails**, the transaction is discarded and none of the actions take effect. :::warning One Receiver Notice that all actions are performed on the same account. It is **not possible** to perform actions on multiple accounts in a single transaction, because transactions can have **only one receiver** ::: --- # Source: https://docs.near.org/protocol/transaction-execution.md --- id: transaction-execution title: Lifecycle of a Transaction description: "Learn how NEAR transactions are executed and finalized" --- `Transactions` are constructed by users to express the intent of performing actions in the network. Once in the network, transactions are converted into `Receipts`, which are messages exchanged between network nodes. On this page, we will explore the lifecycle of a transaction, from its creation to its final status. :::tip Recommended Reading To dig deeper into transaction routing, we recommend reading the [nearcore documentation](https://near.github.io/nearcore/architecture/how/tx_routing.html) ::: --- ## Receipts & Finality Let's walk through the lifecycle of a complex transaction and see how it is processed by the network using blocks as **time units**. #### Block #1: The Transaction Arrives After a transaction arrives, the network takes one block to validate it and transform it into a single `Receipt` that contains all the [actions](./transaction-anatomy.md) to be executed. While creating the `Receipt`, the `signer` gets $NEAR deducted from its balance to **pay for the gas** and **any attached NEAR**. If the `signer` and `receiver` coincide - e.g. the `signer` is adding a Key - the `Receipt` is immediately processed in this first block and the transaction is considered final. #### Block #2: The Receipt is Processed If the `signer` and `receiver` differs - e.g. the `signer` transfers NEAR to the `receiver` - the `Receipt` is processed in a second block. During this process a `FunctionCall` could span a **cross-contract call**, creating one or multiple new `Receipts`. #### Block #3...: Function Calls Each `Receipt` created from the function call take an additional block to be processed. Notice that, if those `Receipts` are `FunctionCall` they could spawn new `Receipts` and so on. #### Final Block: Gas is Refunded A final `Receipt` is processed in a new block, refunding any extra gas paid by the user.
Waiting for a Transaction When using our libraries (or directly contacting the RPC), you can specify how long you want to wait for a transaction to be processed using a `wait_until` parameter. Let's explore the different stages of a transaction's lifecycle. After a transaction is submitted, it first gets validated by the RPC node. If the transaction fails structural checks it will be immediately rejected. Otherwise, it enters the following lifecycle: 1. The transaction has been accepted by the RPC node and is waiting to be included in a block. If `wait_until = None`, the RPC will return a response at this stage. The transaction hasn’t started executing yet, though it will typically start in the next block. 2. Once the transaction reaches a validator, it ensures a signature matches signer access key and that the account is charged gas pre-payment, then updates access key nonce. The transaction is included in a chunk/block and converted into a single receipt for execution. If `wait_until = Included`, the RPC returns a response immediately indicating that the transaction has just started the execution (no execution results, logs, and outcomes are available at this point). 3. In the next blocks, the receipt will get executed and it may produce more receipts for cross-contract interactions. Once all receipts finish the execution, RPC returns a response if `wait_until = ExecutedOptimistic`. 4. The block that contains the transaction has been finalized. This may occur even sooner than receipts execution finishes (depending on how many cross-contract interactions there), and when it finishes, the RPC returns a response if `wait_until = IncludedFinal`. 5. Once the block that contains the transaction has been finalized and all receipts have completed execution (both `IncludedFinal` and `ExecutedOptimistic` are satisfied), then RPC returns a response if `wait_until = Executed`. 6. Once blocks containing the transaction and all of its receipts are finalized, the RPC returns a response at this point if `wait_until = Final`.
:::info A transaction is considered **final** when all its receipts are processed. ::: :::tip Most transactions will just spawn a receipt to process the actions, and a receipt to refund the gas, being final in 1-3 blocks (~1-3 seconds): - [One block](https://testnet.nearblocks.io/txns/8MAvH96aMfDxPb3kVDrgj8nvJS7CAXP1GgtiivKAMGkF#execution) if the `signer` and `receiver` coincide - e.g. when adding a key - [Three blocks](https://testnet.nearblocks.io/txns/B7gxJNxav1A9WhWvaNWYLrSTub1Mkfj3tAudoASVM5tG#) if the `signer` and `receiver` differ, since the first block creates the `Receipt`, and the last reimburses gas Function calls might take longer, as they can spawn multiple receipts. Network congestion can also increase the time to process a receipt and, thus, a transaction. ::: --- ## Transaction Status As the `Receipts` of a `Transaction` are processed, they get a status: - `Success`: the actions on the receipt were executed successfully - `Failed`: an action on the receipt failed - `Unknown`: the receipt is not known by the network If an action in a `Receipt` fails, all the actions in that `Receipt` are rolled back. Notice that we are talking about the `Receipt` status, and not the `Transaction` status. The status of a transaction is determined by its first receipt, which contains all its actions. If any of the actions in the first receipt fail, the transaction is marked as failed. Notice that, it could happen that a transaction is marked as successful, but some of its receipt fails. This happens when a `FunctionCall` successfully spawns a new receipt, but the consequent function call fails. In this case, the transaction is marked as successful because the original function call was successful. See the examples below for more details.
Status Examples #### Example: Transaction with Transfer 1. `bob.near` creates a transaction to transfer 10 NEAR to `alice.near` 2. The transaction is converted into a receipt 3. The conversion fails because `bob.near` does not have enough balance 4. The transaction is marked as failed ⛔ #### Example: Deploying a Contract 1. `bob.near` creates a transaction to: - create the account `contract.bob.near` - transfer 5 NEAR to `contract.bob.near` - deploy a contract in `contract.bob.near` 2. The transaction is transformed into one receipt 3. The account is created, the money transfer and the contract deployed 4. The transaction is marked as successful ✅ #### Example: Deploying a Contract Fails 1. `bob.near` creates a transaction to: - create the account `contract.bob.near` - transfer 5 NEAR to `contract.bob.near` - deploy a contract in `contract.bob.near` 2. The transaction is transformed into one receipt 3. The account is created, but the transfer fails because `bob.near` does not have enough balance 4. The whole process is reverted (i.e. no account is created) 5. The transaction is marked as failed ⛔ #### Example: Calling a Function 1. `bob.near` creates a transaction to call the function `cross-call` in `contract.near` 2. The transaction is transformed into one receipt 3. The function `cross-call` creates a promise to call the function `external-call` in `external.near` 4. The function finishes correctly and the transaction is marked as successful ✅ 5. A new receipt is created to call the function `external-call` in `external.near` 5. The function `external-call` fails 6. The original transaction is still marked as successful ✅ because the first receipt was successful
:::tip You can check the status of a transaction using the [NearBlocks explorer](https://nearblocks.io/) ::: --- ## Nonce values The `nonce` is a number that is incremented for each transaction sent by the transaction's `Signer`. On a valid transaction, the `nonce` value should follow these rules: - When adding a key, the `nonce` is automatically assigned - particularly, it is given the value `block height * 10^6` - so the value in the `ADD_KEY` action is ignored - A transaction is accepted only if the `nonce` value is in the range of: - `[(current_nonce_of_access_key + 1) .. (block_height * 10^6)]` - Once a transaction is accepted, the access key's `nonce` is set to the `nonce` value of the included transaction --- # Source: https://docs.near.org/api/rpc/transactions.md # Source: https://docs.near.org/protocol/transactions.md --- id: transactions title: Transactions description: "Learn how users interact with NEAR through transactions composed of actions, signed with private keys, and processed by the network with deterministic gas costs." --- Users interact with NEAR by creating transactions. Specifically, users use their account's [private keys](./access-keys.md) to sign transactions, which are then broadcasted and processed by the network. ![](@site/static/assets/docs/welcome-pages/data-lake.png) A transaction is composed of one or more [`Actions`](./transaction-anatomy.md), and each action costs a deterministic amount of [gas units](./gas.md). These gas units are translated into a cost in NEAR tokens, which the user must pay for the transaction to be processed. :::tip You can use an Explorer to inspect transactions in the NEAR network ::: --- # Source: https://docs.near.org/tutorials/controlling-near-accounts/transfer.md --- id: transfer title: Transfer Near tokens on behalf of a controlled account sidebar_label: Act on behalf of controllable account description: "Build transaction arguments, request MPC signatures, and broadcast signed NEAR token transfers securely." --- In this part of the tutorial, we'll dig into how transaction arguments are built, how to request a signature according to the arguments from the smart contract, and how to broadcast the signed transaction into the network. ## Building transaction arguments Now that we've set up the account and derived the public key, it's time to build the transaction arguments. For a transaction to be valid, we must attach both a `nonce` and a `recent_block_hash` - these values ensure that the transaction is unique and prevent replay attacks. Let's fetch `nonce` first: ```ts const accessKey = await near.connection.provider.query({ request_type: "view_access_key", account_id: controllableAccountId, // "controllable.testnet" public_key: derivedPublicKey, // derived in the previous chapter finality: "optimistic", }); const nonce = accessKey.nonce; ``` And `recent_block_hash`, now it's your turn: ```ts const block = await near.connection.provider.block({ finality: "final", }); const blockHash = block.header.hash; ``` Now that we have everything needed, let's build transaction arguments: ```ts const transactionArgs = { signer_id: controllableAccountId, // "controllable.testnet" signer_pk: derivedPublicKey, // derived in the previous chapter nonce: (nonce + 1).toString(), block_hash: blockHash, }; ``` ## Requesting signature from Smart Contract After building transaction arguments, the next step is to request a signature from the smart contract. ```ts const adminAccount = await near.account("admin.testnet"); const outcome = await adminAccount.functionCall({ contractId: contractId, // "broken-rock.testnet" methodName: "transfer_on_behalf_of", args: { args: transactionArgs, }, gas: "300000000000000", attachedDeposit: "100000000000000000000000", // 0.1 NEAR is enough in most cases to pay MPC fee }); ``` ## Broadcasting the transaction With the signature in hand, we are now ready to send it to the network! ```ts // Get the signed transaction from the outcome result = providers.getTransactionLastResult(outcome); const signedTx = new Uint8Array(result); // Send the signed transaction const transferOutcome = await near.connection.provider.sendJsonRpc( "broadcast_tx_commit", [Buffer.from(signedTx).toString("base64")] ); console.log( `https://nearblocks.io/txns/${transferOutcome.transaction_outcome.id}` ); ``` ## Final Thoughts With that, we've successfully completed the full journey of acting on behalf of another Near account securely and transferred to yourself 0.1 NEAR from a controllable account. By following this process, you now have a solid understanding of how to manage external accounts on Near using Multi-Party Computation (MPC). --- # Source: https://docs.near.org/ai/shade-agents/tutorials/tutorials-overview.md --- id: tutorials-overview title: Tutorials and Templates sidebar_label: Overview description: "Review the list of our Shade Agent tutorials and templates." --- This section provides tutorials and templates to help you build Shade Agents faster and learn key concepts. --- ## Quickstart Explore the [Quickstart docs](../getting-started/quickstart/deploying.md) ### Summary The Quickstart features a verifiable Oracle secured by the Shade Agent Framework that pushes the price of ETH to a smart contract on Ethereum. ### Template The Quickstart provides a basic template for building your first multichain Shade Agent. This is the recommended starting point for developers new to Shade Agents. ### Learning Outcomes - Learn how to get started building with Shade Agents - Learn the key components of a Shade Agent - Learn to deploy a Shade Agent - Learn how to sign transactions for different chains --- ## Verifiable AI DAO Explore the [Verifiable AI DAO docs](./ai-dao/overview.md) ### Summary The Verifiable AI DAO is a DAO smart contract that uses a Shade Agent with a verifiable LLM to vote on governance proposals according to its predefined manifesto, ensuring transparent and auditable AI-driven governance decisions. ### Template The Verifiable AI DAO is a template for building yield and resume-based Shade Agents. ### Learning Outcomes - Learn to build a Shade Agent that uses the yield and resume pattern - Learn to develop a Shade Agent that operates exclusively on the NEAR blockchain - Learn to use NEAR AI's verifiable/private inference within a Shade Agent --- # Source: https://docs.near.org/smart-contracts/anatomy/types.md --- id: types title: SDK Types hide_table_of_contents: true description: "Learn everything the SDK has to offer to efficiently store data." --- Lets discuss which types smart contracts use to input and output data, as well as how such data is stored and handled in the contract's code. ### Native Types Smart contracts can receive, store and return data using JS native types: - `string` - `number` - `boolean` - `Array` - `Map` - `Object` - `BigInt` ### Native Types Smart contracts can receive, store and return data using the following Rust types: - `string` - `i8-i32/u8-u32` - **`u64/128`**: It is preferable to use SDK types `U64` and `U128` - `bool` - `HashMap` - `Vector` ### Native Types Smart contracts can receive, store and return data using the following Python types: - `str` - `int` - `float` - `bool` - `list` - `dict` - `set` - `bytes` - `None` :::warning `U64/U128` Smart contracts can store `u64` and `u128`, but these types need to be converted to `string` for input/output, since JSON cannot serialize values with more than 52 bits To simplify development, the SDK provides the `U64` and `U128` types which are automatically casted to `u64/u128` when stored, and to `string` when used as input/output ::: :::tip Python Large Numbers Python's `int` type has unlimited precision, so it can handle large integers (like yoctoNEAR values) without any special handling. All values are automatically serialized and deserialized correctly. ::: ### Complex Objects Smart contracts can store and return complex objects **Note:** Objects will always be received and returned as JSON #### Serializers Objects that will be used as input or output need to be serializable to JSON, add the `#[near(serializer=json)]` macro Objects that will be stored in the contract's state need to be serializable to Borsh, add the `#[near(serializer=borsh)]` macro #### Serialization Python objects are automatically serialized and deserialized using JSON for input/output and Pickle for internal storage. Complex nested objects like lists and dictionaries can be used directly without additional configuration. ### Handling Tokens `$NEAR` tokens are typed as `BigInt` in JS, and their values represented in `yoctonear` **Note:** 1 NEAR = 10^24 yoctoNEAR ### Handling Tokens `$NEAR` tokens are handled through the `NearToken` struct, which exposes methods to represent the value in `yoctonear`, `milinear` and `near` **Note:** 1 NEAR = 10^24 yoctonear ### Handling Tokens `$NEAR` tokens are represented as integers in Python, with values in `yoctoNEAR`. The `near_sdk_py.constants` module provides `ONE_NEAR` and `ONE_TGAS` constants. **Note:** 1 NEAR = 10^24 yoctoNEAR ### Account The SDK exposes a special type to handle NEAR Accounts, which automatically checks if the account address is valid ### Account IDs In Python, NEAR account IDs are represented as `str` types. The SDK performs validation when account IDs are used in contract calls or cross-contract interactions. ```js import { NearBindgen, near, call, view } from 'near-sdk-js'; @NearBindgen({}) class HelloNear { greeting: string = 'Hello'; static schema = { // JS contracts need a schema "greeting": "string" }; @view({}) // This method is read-only and can be called for free get_greeting(): string { return this.greeting; } @call({}) // This method changes the state, for which it cost gas set_greeting({ greeting }: { greeting: string }): void { near.log(`Saving greeting ${greeting}`); ``` ```rust use near_sdk::{log, near}; // Define the contract structure #[near(contract_state)] pub struct Contract { greeting: String, } // Define the default, which automatically initializes the contract impl Default for Contract { fn default() -> Self { Self { greeting: "Hello".to_string(), } } } // Implement the contract structure #[near] impl Contract { // Public method - returns the greeting saved, defaulting to DEFAULT_GREETING pub fn get_greeting(&self) -> String { self.greeting.clone() } // Public method - accepts a greeting, such as "howdy", and records it pub fn set_greeting(&mut self, greeting: String) { log!("Saving greeting: {}", greeting); self.greeting = greeting; } } ``` ```python from near_sdk_py import view, call, Contract # Define contract class class HelloNear: @init def new(self): # Initialize state with default greeting self.storage["greeting"] = "Hello" @view def get_greeting(self) -> str: """Returns the current greeting""" return self.storage["greeting"] @call def set_greeting(self, message: str) -> str: """Sets a new greeting""" self.storage["greeting"] = message ``` ```js import { NearBindgen, near, call, view, AccountId, NearPromise, initialize, assert } from "near-sdk-js"; class Bid { bidder: AccountId; bid: bigint; } @NearBindgen({ requireInit: true }) class AuctionContract { highest_bid: Bid = { bidder: '', bid: BigInt(0) }; auction_end_time: bigint = BigInt(0); auctioneer: AccountId = ""; claimed: boolean = false; @initialize({ privateFunction: true }) init({ end_time, auctioneer}: { end_time: bigint, auctioneer: AccountId}) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; this.auctioneer = auctioneer; } @call({ payableFunction: true }) bid(): NearPromise { // Assert the auction is still ongoing assert(this.auction_end_time > near.blockTimestamp(), "Auction has ended"); // Current bid const bid = near.attachedDeposit(); const bidder = near.predecessorAccountId(); // Last bid const { bidder: lastBidder, bid: lastBid } = this.highest_bid; // Check if the deposit is higher than the current bid assert(bid > lastBid, "You must place a higher bid"); // Update the highest bid this.highest_bid = { bidder, bid }; // Save the new bid // Transfer tokens back to the last bidder return NearPromise.new(lastBidder).transfer(lastBid); } @call({}) claim() { assert(this.auction_end_time <= near.blockTimestamp(), "Auction has not ended yet"); assert(!this.claimed, "Auction has been claimed"); this.claimed = true; return NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid) } @view({}) get_highest_bid(): Bid { return this.highest_bid; } @view({}) get_auction_end_time(): BigInt { return this.auction_end_time; } ``` ```rust use near_sdk::json_types::U64; use near_sdk::{env, near, require, AccountId, NearToken, PanicOnDefault, Promise}; #[near(serializers = [json, borsh])] #[derive(Clone)] pub struct Bid { pub bidder: AccountId, pub bid: NearToken, } #[near(contract_state)] #[derive(PanicOnDefault)] pub struct Contract { highest_bid: Bid, auction_end_time: U64, auctioneer: AccountId, claimed: bool, } #[near] impl Contract { #[init] #[private] // only callable by the contract's account pub fn init(end_time: U64, auctioneer: AccountId) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: NearToken::from_yoctonear(1), }, auction_end_time: end_time, claimed: false, auctioneer, } } #[payable] pub fn bid(&mut self) -> Promise { // Assert the auction is still ongoing require!( env::block_timestamp() < self.auction_end_time.into(), "Auction has ended" ); // Current bid let bid = env::attached_deposit(); let bidder = env::predecessor_account_id(); // Last bid let Bid { bidder: last_bidder, bid: last_bid, } = self.highest_bid.clone(); // Check if the deposit is higher than the current bid require!(bid > last_bid, "You must place a higher bid"); // Update the highest bid self.highest_bid = Bid { bidder, bid }; // Transfer tokens back to the last bidder Promise::new(last_bidder).transfer(last_bid) } pub fn claim(&mut self) -> Promise { require!( env::block_timestamp() > self.auction_end_time.into(), "Auction has not ended yet" ); require!(!self.claimed, "Auction has already been claimed"); self.claimed = true; // Transfer tokens to the auctioneer Promise::new(self.auctioneer.clone()).transfer(self.highest_bid.bid) } pub fn get_highest_bid(&self) -> Bid { self.highest_bid.clone() } pub fn get_auction_end_time(&self) -> U64 { self.auction_end_time } ``` ```python class AuctionContract(Contract): @init def initialize(self, end_time, auctioneer): """Initialize the auction contract with an end time and auctioneer account""" # Save auction end time self.storage["auction_end_time"] = end_time # Initialize highest bid with current account and 1 yocto initial_bid = { "bidder": self.current_account_id, "bid": ONE_YOCTO } self.storage["highest_bid"] = initial_bid # Set auctioneer self.storage["auctioneer"] = auctioneer # Initialize claimed status self.storage["claimed"] = False return {"success": True} @call def bid(self): """Place a bid in the auction""" # Get auction end time auction_end_time = self.storage["auction_end_time"] # Assert the auction is still ongoing current_time = self.block_timestamp if current_time >= auction_end_time: raise InvalidInput("Auction has ended") # Current bid bid_amount = self.attached_deposit bidder = self.predecessor_account_id # Get last bid highest_bid = self.storage["highest_bid"] last_bidder = highest_bid["bidder"] last_bid = highest_bid["bid"] # Check if the deposit is higher than the current bid if bid_amount <= last_bid: raise InvalidInput("You must place a higher bid") # Update the highest bid new_highest_bid = { "bidder": bidder, "bid": bid_amount } self.storage["highest_bid"] = new_highest_bid # Log the new bid self.log_event("new_bid", { "bidder": bidder, "amount": bid_amount }) # Transfer tokens back to the last bidder return CrossContract(last_bidder).transfer(last_bid).value() @call def claim(self): """Claim the auction proceeds after it has ended""" # Get auction end time and claimed status auction_end_time = self.storage["auction_end_time"] claimed = self.storage.get("claimed", False) # Assert the auction has ended current_time = self.block_timestamp if current_time <= auction_end_time: raise InvalidInput("Auction has not ended yet") # Assert the auction has not been claimed if claimed: raise InvalidInput("Auction has already been claimed") # Mark as claimed self.storage["claimed"] = True # Get highest bid and auctioneer highest_bid = self.storage["highest_bid"] auctioneer = self.storage["auctioneer"] # Log the claim self.log_event("auction_claimed", { "winner": highest_bid["bidder"], "amount": highest_bid["bid"] }) # Transfer tokens to the auctioneer return CrossContract(auctioneer).transfer(highest_bid["bid"]).value() @view def get_highest_bid(self): """Returns the current highest bid information""" return self.storage.get("highest_bid") @view def get_auction_end_time(self): """Returns the auction end time""" return self.storage.get("auction_end_time") @view def get_auction_status(self): """Returns the overall status of the auction""" current_time = self.block_timestamp auction_end_time = self.storage["auction_end_time"] highest_bid = self.storage["highest_bid"] claimed = self.storage.get("claimed", False) return { "is_active": current_time < auction_end_time, "time_remaining": max(0, auction_end_time - current_time), "highest_bidder": highest_bid["bidder"], "highest_bid": highest_bid["bid"], "claimed": claimed, "auctioneer": self.storage["auctioneer"] } ``` --- # Source: https://docs.near.org/smart-contracts/testing/unit-test.md --- id: unit-test title: Unit Testing description: "Learn how to write and run unit tests for NEAR smart contracts to test individual methods and functions in isolation." --- Unit tests allow you to test the contract methods individually. They are suitable to check the storage is updated correctly, and that methods return their expected values. They are written in the contract's language and execute locally. If you used one of our [examples](https://github.com/near-examples/docs-examples) as template, then you simply need to navigate to the contract's folder, and use `yarn test`. In case you didn't, then we recommend you copy the necessary node files (e.g. `package.json`) from one of our templates. :::tip You can run `yarn test` from the root folder of each project to run both unit and [integration](integration-test.md) tests. ::: --- ## Snippet I: Testing a Counter The tests in the [Counter Example](https://github.com/near-examples/counters) rely on basic functions to check that the `increment`, `decrement`, and `reset` methods work properly. ``` #[test] fn increment() { // instantiate a contract variable with the counter at zero let mut contract = Counter { val: 0 }; contract.increment(None); assert_eq!(1, contract.get_num()); } #[test] fn increment_with_points() { // instantiate a contract variable with the counter at zero let mut contract = Counter { val: 0 }; contract.increment(Some(10)); assert_eq!(10, contract.get_num()); } #[test] fn decrement() { let mut contract = Counter { val: 0 }; contract.decrement(None); assert_eq!(-1, contract.get_num()); } ``` ``` [dev-dependencies] near-sdk = { version = "5.24.0", features = ["unit-testing"] } near-sandbox = "0.3" ``` --- ## Snippet II: Modifying the Context While doing unit testing you can modify the [Environment variables](../anatomy/environment.md) through the `VMContextBuilder`. This will enable you to, for example, simulate calls from different users, with specific attached deposit and GAS. Here we present a snippet on how we test the `donate` method from our [Donation Example](https://github.com/near-examples/donation-examples) by manipulating the `predecessor` and `attached_deposit`. ``` #[test] fn donate() { let mut contract = Contract::init(BENEFICIARY.parse().unwrap()); // Make a donation set_context("donor_a", ONE_NEAR); contract.donate(); let first_donation = contract.get_donation_for_account("donor_a".parse().unwrap()); // Check the donation was recorded correctly assert_eq!( u128::from(first_donation.total_amount), ONE_NEAR.as_yoctonear() ); // Make another donation set_context("donor_b", ONE_NEAR.saturating_mul(2)); contract.donate(); let second_donation = contract.get_donation_for_account("donor_b".parse().unwrap()); // Check the donation was recorded correctly assert_eq!( u128::from(second_donation.total_amount), ONE_NEAR.saturating_mul(2).as_yoctonear() ); // User A makes another donation on top of their original set_context("donor_a", ONE_NEAR); contract.donate(); let first_donation = contract.get_donation_for_account("donor_a".parse().unwrap()); // Check the donation was recorded correctly assert_eq!( u128::from(first_donation.total_amount), ONE_NEAR.saturating_mul(2).as_yoctonear() ); assert_eq!(u64::from(contract.number_of_donors()), 2); } // Auxiliar fn: create a mock context fn set_context(predecessor: &str, amount: NearToken) { let mut builder = VMContextBuilder::new(); builder.predecessor_account_id(predecessor.parse().unwrap()); builder.attached_deposit(amount); testing_env!(builder.build()); } } ``` ``` [dev-dependencies] near-sdk = { version = "5.7.0", features = ["unit-testing"] } near-workspaces = { version = "0.16.0", features = ["unstable"] } ``` --- ## ⚠️ Limitations Unit tests are useful to check for code integrity, and detect basic errors on isolated methods. However, since unit tests do not run on a blockchain, there are many things which they cannot detect. Unit tests are not suitable for: - Testing [gas](../anatomy/environment.md) and [storage](../anatomy/storage.md) usage - Testing [transfers](../anatomy/actions.md) - Testing [cross-contract calls](../anatomy/crosscontract.md) - Testing complex interactions, i.e. multiple users depositing money on the contract For all these cases it is necessary to **complement** unit tests with [integration tests](integration-test.md). --- # Source: https://docs.near.org/tutorials/examples/update-contract-migrate-state.md --- id: update-contract-migrate-state title: Self Upgrade & State Migration description: "Learn NEAR smart contract upgrades, including self-updating contracts, state migration, and versioning patterns." --- Three examples on how to handle updates and [state migration](../../smart-contracts/release/upgrade.md): 1. [State Migration](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates): How to implement a `migrate` method to migrate state between contract updates. 2. [State Versioning](https://github.com/near-examples/update-migrate-rust/tree/main/enum-updates): How to use readily use versioning on a state, to simplify updating it later. 3. [Self Update](https://github.com/near-examples/update-migrate-rust/tree/main/self-updates): How to implement a contract that can update itself. --- ## State Migration The [State Migration example](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates) shows how to handle state-breaking changes between contract updates. It is composed by 2 contracts: 1. Base: A Guest Book where people can write messages. 2. Update: An update in which we remove a parameter and change the internal structure. ``` #[private] #[init(ignore_state)] pub fn migrate() -> Self { // retrieve the current state from the contract let mut old_state: OldState = env::state_read().expect("failed"); // new messages vector to hold the migrated messages let mut new_messages: Vector = Vector::new(MESSAGES_PREFIX); // iterate through the state migrating it to the new version for (idx, posted) in old_state.messages.iter().enumerate() { // get the payment and remove it from the old state payments vector so it won't be left in the new state let payment = old_state.payments.get(idx as u64) .expect("failed to get payment") .clone(); // push the new message to the new messages vector new_messages.push(&PostedMessage { payment, premium: posted.premium, sender: posted.sender.clone(), text: posted.text.clone(), }) } // remove the payments from the old state old_state.payments.clear(); // return the new state ``` #### The Migration Method The migration method deserializes the current state (`OldState`) and iterates through the messages, updating them to the new `PostedMessage` that includes the `payment` field. :::tip Notice that migrate is actually an [initialization method](../../smart-contracts/anatomy/storage.md) that ignores the existing state (`[#init(ignore_state)]`), thus being able to execute and rewrite the state. ::: --- ## State Versioning The [State Versioning example](https://github.com/near-examples/update-migrate-rust/tree/main/enum-updates) shows how to use [Enums](https://doc.rust-lang.org/book/ch06-01-defining-an-enum.html) to implement state versioning on a contract. Versioning simplifies updating the contract since you only need to add a new version of the structure. All versions can coexist, thus you will not need to change previously existing structures. The example is composed by 2 contracts: 1. Base: The Guest Book contract using versioned `PostedMessages` (`PostedMessagesV1`). 2. Update: An update that adds a new version of `PostedMessages` (`PostedMessagesV2`). ``` #[near(serializers=[borsh])] pub enum VersionedPostedMessage { V1(PostedMessageV1), V2(PostedMessageV2), } impl From for PostedMessageV2 { fn from(message: VersionedPostedMessage) -> Self { match message { VersionedPostedMessage::V2(posted) => posted, VersionedPostedMessage::V1(posted) => PostedMessageV2 { payment: NearToken::from_near(0), premium: posted.premium, sender: posted.sender, text: posted.text, }, } } } ``` --- ## Self Update The [Self Update example](https://github.com/near-examples/update-migrate-rust/tree/main/self-updates) shows how to implement a contract that can update itself. It is composed by 2 contracts: 1. Base: A Guest Book were people can write messages, implementing a `update_contract` method. 2. Update: An update in which we remove a parameter and change the internal structure. ``` pub fn update_contract(&self) -> Promise { // Check the caller is authorized to update the code assert!( env::predecessor_account_id() == self.manager, "Only the manager can update the code" ); // Receive the code directly from the input to avoid the // GAS overhead of deserializing parameters let code = env::input().expect("Error: No input").to_vec(); // Deploy the contract on self Promise::new(env::current_account_id()) .deploy_contract(code) .function_call( "migrate".to_string(), NO_ARGS, NearToken::from_near(0), CALL_GAS, ) .as_return() } } ``` --- # Source: https://docs.near.org/tutorials/auction/updating-the-frontend.md --- id: updating-the-frontend title: Updating the Frontend description: "Update the frontend to display the new token information." --- Now we've updated the contract to include an NFT as a reward and changed the contract such that it accepts bids in fungible tokens, we need to update the frontend accordingly. ## Getting the data from the contract Now we have a function to output the whole contract state we will call this function in our frontend ``` const getInfo = async () => { const data = await wallet.viewMethod({ contractId: AUCTION_CONTRACT, method: "get_auction_info", }); setAuctionInfo(data) } getInfo(); ``` This call will deliver us the contract Ids of the FT and NFT contracts along with the token Id of the NFT. We will then use this information to call the `ft_metadata` and `nft_token` methods on the FT and NFT contracts respectively to get information about the FT and NFT. --- ## Displaying the NFT We want to show what NFT is being auctioned. To do this we will call `nft_token` on the NFT contract to get the NFT metadata. To call this method we need to specify the NFT `contractId` and the `token_id`, which can be found in the auction information. `nft_token` also returns the owner of the NFT, so we'll check this against the contract account to verify that the auction is valid. ``` useEffect(() => { const getNftInfo = async () => { const data = await wallet.viewMethod({ contractId: auctionInfo.nft_contract, method: "nft_token", args: { token_id: auctionInfo.token_id } }); setNftInfo(data) if (data.owner_id == AUCTION_CONTRACT) { setValidAuction("Valid Auction") } } if (auctionInfo) { getNftInfo(); } }, [auctionInfo]); ``` Note that this effect will only run once the `auctionInfo` updates because we first need the NFT contract ID and token ID from `auctionInfo` to make a valid call to `nft_token`. In a new component named `AuctionItem` we display the NFT image, name, and description. ``` import styles from './AuctionItem.module.css'; const AuctionItem = ({ nftMetadata, validAuction }) => { const cardImage = nftMetadata?.media ? `https://image-cache-service-z3w7d7dnea-ew.a.run.app/media?url=https://arweave.net/${nftMetadata.media}` : null; return (

{validAuction}

{nftMetadata?.title}

{nftMetadata?.description}

NFT
); } export default AuctionItem; ```
Note that an image caching service is used to display the NFT image for better performance. --- ## Fetching FT information Using the FT contract ID from the auction information, we can call the `ft_metadata` method on the FT contract to get information about the fungible token that is being used for the auction. ``` useEffect(() => { const getFtInfo = async () => { const ftInfo = await wallet.viewMethod({ contractId: auctionInfo.ft_contract, method: "ft_metadata", }); setFtContract(auctionInfo.ft_contract) setFtName(ftInfo.symbol) setFtImg(ftInfo.icon) setFtDecimals(ftInfo.decimals) let bidAmount = auctionInfo.highest_bid.bid / Math.pow(10, ftInfo.decimals) setLastBidDisplay(bidAmount) fetchPastBids(); } if (auctionInfo) { getFtInfo(); } }, [auctionInfo]); ``` We set the FT image, symbol, icon, and decimals in state. We use the decimals to format the amount of tokens being bid. In the case of DAI it divides the amount by 10^18. The reverse process is used when making a bid, the bid amount is multiplied by 10^18 before being sent to the contract. --- ## Bidding with FTs Instead of calling the function `bid` on the contract we now call the `ft_transfer_call` function on the FT contract. This function transfers the FTs to the auction contract and calls the `ft_on_transfer` on the auction contract. ``` const bid = async (amount) => { let real_amount = amount * Math.pow(10, ftDecimals) let response = await wallet.callMethod({ contractId: auctionInfo.ft_contract, method: "ft_transfer_call", deposit: 1, args: { "receiver_id": AUCTION_CONTRACT, "amount": String(real_amount), "msg": "" }, gas:"300000000000000" }) return response } ``` --- ## Updating the indexing API call We need to update the API call that fetches historical bids to now index each time `ft_on_transfer` is called on the auction contract from the FT contract. ``` export default async function handler(req, res) { try { if (!process.env.API_KEY) { return res.status(500).json({ error: "API key not provided" }); } // Get all bid transactions const { contractId, ftId } = req.query; const bidsRes = await fetch(`https://api-testnet.nearblocks.io/v1/account/${contractId}/txns?from=${ftId}&method=ft_on_transfer&page=1&per_page=25&order=desc`, { headers: { 'Accept': '*/*', 'Authorization': `Bearer ${process.env.API_KEY}` } }); ``` And now instead of getting the bid amount from the deposit, it is now retrieved from the calls argument, from `amount`. The case is the same for the account Id of the bidder, from `sender_id`. ``` if (txn.receipt_outcome.status) { let args = txn.actions[0].args; let parsedArgs = JSON.parse(args); let amount = Number(parsedArgs.amount); let account = parsedArgs.sender_id; ``` --- ## Conclusion Ok nice, that didn't take too long. To look back, we updated the frontend to now display the NFT being auctioned, to display bid amounts - both the current and historical bids - in terms of the FT being used, and changed the bidding process to now use FTs. In the [final section](./4-factory.md) of this mega tutorial we'll create an auction factory contract that is used to deploy and initialize new auction contracts. --- # Source: https://docs.near.org/smart-contracts/release/upgrade.md --- id: upgrade title: Updating Contracts description: "Learn how to upgrade NEAR smart contracts safely, including programmatic updates, migration strategies, and best practices for contract versioning." --- Learn how to update NEAR smart contracts, both through tools like NEAR CLI and programmatically. Understand the implications of state migration when changing contract logic. NEAR accounts separate their logic (contract's code) from their state (storage), allowing the code to be changed. Contract's can be updated in two ways: 1. **Through tools** such as [NEAR CLI](../../tools/cli.md) or the [NEAR API](../../tools/near-api.md) (if you hold the account's [full access key](../../protocol/access-keys.md)). 2. **Programmatically**, by implementing a method that [takes the new code and deploys it](#programmatic-update). --- ## Updating Through Tools Simply re-deploy another contract using your preferred tool, for example, using [NEAR CLI](../../tools/cli.md): ```bash # (optional) If you don't have an account, create one near create-account --useFaucet # Deploy the contract near deploy ``` --- ## Programmatic Update A smart contract can also update itself by implementing a method that: 1. Takes the new wasm contract as input 2. Creates a Promise to deploy it on itself ``` pub fn update_contract(&self) -> Promise { // Check the caller is authorized to update the code assert!( env::predecessor_account_id() == self.manager, "Only the manager can update the code" ); // Receive the code directly from the input to avoid the // GAS overhead of deserializing parameters let code = env::input().expect("Error: No input").to_vec(); // Deploy the contract on self Promise::new(env::current_account_id()) .deploy_contract(code) .function_call( "migrate".to_string(), NO_ARGS, NearToken::from_near(0), CALL_GAS, ) .as_return() } } ``` #### How to Invoke Such Method? ```bash # Call the update_contract method near contract call-function as-transaction update_contract file-args
prepaid-gas '300.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send ``` ```js // Load the contract's raw bytes const code = fs.readFileSync("./path/to/wasm.wasm"); // Call the update_contract method await wallet.callFunction({ contractId: guestBook, method: "update_contract", args: code, gas: "300000000000000", }); ``` :::tip DAO Factories This is how DAO factories [update their contracts](https://github.com/near-daos/sputnik-dao-contract/blob/main/sputnikdao-factory2/src/factory_manager.rs#L60) ::: --- ## Migrating the State Since the account's logic (smart contract) is separated from the account's state (storage), **the account's state persists** when re-deploying a contract. Because of this, **adding methods** or **modifying existing ones** will yield **no problems**. However, deploying a contract that **modifies or removes structures** stored in the state will raise an error: `Cannot deserialize the contract state`, in which case you can choose to: 1. Use a different account 2. Rollback to the previous contract code 3. Add a method to migrate the contract's state
### The Migration Method If you have no option but to migrate the state, then you need to implement a method that: 1. Reads the current state of the contract 2. Applies different functions to transform it into the new state 3. Returns the new state :::tip DAO Update This is how DAOs [update themselves](https://github.com/near-daos/sputnik-dao-contract/blob/main/sputnikdao2/src/upgrade.rs#L59) :::
### Example: Guest Book Migration Imagine you have a Guest Book where you store messages, and the users can pay for such messages to be "premium". You keep track of the messages and payments using the following state: ``` const MESSAGES_PREFIX: &[u8] = b"m"; const PAYMENTS_PREFIX: &[u8] = b"p"; #[near(serializers=[json, borsh])] pub struct PostedMessage { pub premium: bool, pub sender: AccountId, pub text: String, } #[near(contract_state)] pub struct GuestBook { messages: Vector, ``` ``` class PostedMessage { constructor() { this.premium = false; this.sender = ""; this.text = ""; } static new(premium, sender, text) { let posted_message = new PostedMessage(); posted_message.premium = premium; posted_message.sender = sender; posted_message.text = text; return posted_message; } } @NearBindgen({}) export class GuestBook { constructor() { this.messages = new Vector("a"); this.payments = new Vector("b"); } ``` #### Update Contract At some point you realize that you could keep track of the `payments` inside of the `PostedMessage` itself, so you change the contract to: ``` const MESSAGES_PREFIX: &[u8] = b"m"; #[near(serializers=[json, borsh])] pub struct PostedMessage { pub payment: NearToken, pub premium: bool, pub sender: AccountId, pub text: String, } #[near(contract_state)] pub struct GuestBook { messages: Vector, ``` ``` class PostedMessage { constructor() { this.payment = 0n; this.premium = false; this.sender = ""; this.text = ""; } static new(payment, premium, sender, text) { let posted_message = new PostedMessage(); posted_message.payment = payment; posted_message.premium = premium; posted_message.sender = sender; posted_message.text = text; return posted_message; } } @NearBindgen({}) export class GuestBook { constructor() { this.messages = new Vector("a"); } ``` #### Incompatible States If you deploy the update into an initialized account the contract will fail to deserialize the account's state, because: 1. There is an extra `payments` vector saved in the state (from the previous contract) 2. The stored `PostedMessages` are missing the `payment` field (as in the previous contract) #### Migrating the State To fix the problem, you need to implement a method that goes through the old state, removes the `payments` vector and adds the information to the `PostedMessages`: ``` #[near(serializers=[borsh])] pub struct OldPostedMessage { pub premium: bool, pub sender: AccountId, pub text: String, } #[near(serializers=[borsh])] pub struct OldState { messages: Vector, payments: Vector, } #[near] impl GuestBook { #[private] #[init(ignore_state)] pub fn migrate() -> Self { // retrieve the current state from the contract let mut old_state: OldState = env::state_read().expect("failed"); // new messages vector to hold the migrated messages let mut new_messages: Vector = Vector::new(MESSAGES_PREFIX); // iterate through the state migrating it to the new version for (idx, posted) in old_state.messages.iter().enumerate() { // get the payment and remove it from the old state payments vector so it won't be left in the new state let payment = old_state.payments.get(idx as u64) .expect("failed to get payment") .clone(); // push the new message to the new messages vector new_messages.push(&PostedMessage { payment, premium: posted.premium, sender: posted.sender.clone(), text: posted.text.clone(), }) } // remove the payments from the old state old_state.payments.clear(); // return the new state Self { messages: new_messages, } } } ``` ``` class OldPostedMessage { constructor() { this.premium = false; this.sender = ""; this.text = ""; } } @NearBindgen({}) export class OldState { constructor() { this.messages = new Vector("a"); this.payments = new Vector("b"); } } class PostedMessage { constructor() { this.payment = 0n; this.premium = false; this.sender = ""; this.text = ""; } static new(payment, premium, sender, text) { let posted_message = new PostedMessage(); posted_message.payment = payment; posted_message.premium = premium; posted_message.sender = sender; posted_message.text = text; return posted_message; } } @NearBindgen({}) export class GuestBook { constructor() { this.messages = new Vector("a"); } @migrate({}) migrateState() { assert(this.messages !== undefined, "Contract state should not be deserialized in @migrate"); // retrieve the current state from the contract const _state = OldState._getState(); const _contract = OldState._create(); if (_state) { OldState._reconstruct(_contract, _state); } let new_messages = new Vector("p"); _contract.messages.toArray().forEach((posted, idx) => { let payment = _contract .payments .get(idx) || 0n; new_messages.push(PostedMessage.new(payment, posted.premium, posted.sender, posted.text)); }); _contract.messages.clear(); _contract.payments.clear(); this.messages = new_messages; } ``` Notice that `migrate` is actually an [initialization method](../anatomy/storage.md) that **ignores** the existing state (`[#init(ignore_state)]`), thus being able to execute and rewrite the state.
Why we should remove old structures from the state? To understand why we should remove old structures from the state let's take a look to how the data is stored. For example, if the old version of the contract stores two messages with payments according methods `get_messages` and `get_payments` will return the following results:
get_messags result ```bash INFO --- Result ------------------------- | [ | { | "premium": false, | "sender": "test-ac-1719933221123-3.testnet", | "text": "Hello" | }, | { | "premium": false, | "sender": "test-ac-1719933221123-3.testnet", | "text": "Hello" | } | ] | ------------------------------------ ```
get_payments result ```bash INFO --- Result ------------------------- | [ | "10000000000000000000000", | "10000000000000000000000" | ] | ------------------------------------ ```
But if we take a look at the storage as text using following command, we will see that each payment is stored under its own key started with `p\` prefix. ```bash near contract view-storage all as-text network-config testnet now ```
Storage as text result ```bash INFO Contract state (values): | key: STATE | value: \x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00m\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00p | -------------------------------- | key: m\x00\x00\x00\x00\x00\x00\x00\x00 | value: \x00\x1f\x00\x00\x00test-ac-1719933221123-3.testnet\x05\x00\x00\x00Hello | -------------------------------- | key: m\x01\x00\x00\x00\x00\x00\x00\x00 | value: \x00\x1f\x00\x00\x00test-ac-1719933221123-3.testnet\x05\x00\x00\x00Hello | -------------------------------- | key: p\x00\x00\x00\x00\x00\x00\x00\x00 | value: \x00\x00@\xb2\xba\xc9\xe0\x19\x1e\x02\x00\x00\x00\x00\x00\x00 | -------------------------------- | key: p\x01\x00\x00\x00\x00\x00\x00\x00 | value: \x00\x00@\xb2\xba\xc9\xe0\x19\x1e\x02\x00\x00\x00\x00\x00\x00 | -------------------------------- ```
That means that while migrating the state to a new version we need not only change the messages structure, but also remove all payments related keys from the state. Otherwise, the old keys will simply stay behind being orphan, still occupying space. To remove them in `migrate` method, we call `clear()` method on payments vector in mutable `old_state` struct. This method removes all elements from the collection.
:::tip You can follow a migration step by step in the [official migration example](https://github.com/near-examples/update-migrate-rust/tree/main/basic-updates/base) Javascript migration example testfile can be found on here: [test-basic-updates.ava.js](https://github.com/near/near-sdk-js/blob/develop/examples/__tests__/test-basic-updates.ava.js), run by this command: `pnpm run test:basic-update` in examples directory. ::: --- # Source: https://docs.near.org/ai/using-llms.md --- id: using-llms title: Using NEAR documentation with your AI Coding Agents sidebar_label: Docs with AI Coding Agents description: "Using llms.txt to improve your workflow when using AI coding agents." --- NEAR Docs hosts a [`llms.txt`](https://docs.near.org/llms.txt) file to provide information to help LLMs use the complete NEAR documentation at inference time. While websites serve both human readers and LLMs, LLMs benefit from concise information gathered in a single, accessible location. This is critical for use cases like development environments, where LLMs need quick access to programming documentation and APIs. :::info If you want to learn more about the `llms.txt` standard, check [this website](https://llmstxt.org/). ::: ## Using `llms.txt` The `llms.txt` core purpose is to provide context that the AI agent can reference before generating or modifying code. Using an `llms.txt` file as a reference for AI coding agents is an advanced technique to guide the AI's behavior, improve code quality, and enforce project-specific patterns. It's essentially a way to provide contextual, in-the-flow documentation. Next, you can find examples of how to integrate `llms.txt` with [Visual Studio Code](#visual-studio-code) and [Cursor](#cursor). ## Visual Studio Code To use [NEAR Docs llms.txt](https://docs.near.org/llms.txt) as reference when working with Copilot Chat, follow these steps. 1. Open the Copilot Chat sidebar on VS Code. 2. Ask your question, and use the `#fetch` keyword to add `https://docs.near.org/llms.txt` as reference. For example: ``` How can I upgrade a contract state? #fetch https://docs.near.org/llms.txt ``` ![VS Code](/assets/docs/tools/llm-vscode1.png) 3. Get your AI-generated answer, along with direct reference links from the NEAR documentation site. ![VS Code](/assets/docs/tools/llm-vscode2.png) ## Cursor When using Cursor, you can add [NEAR Docs llms.txt](https://docs.near.org/llms.txt) as documentation reference following these steps, so it will be available when asking questions to the AI agent. 1. Open Cursor Chat window. 2. Click on `@`, select `Docs`, and `+ Add new doc`. On the text box, enter: ``` https://docs.near.org/llms.txt ``` ![VS Code](/assets/docs/tools/llm-cursor1.png) 2. You can now select `NEAR Protocol` as documentation reference when asking your questions. ![VS Code](/assets/docs/tools/llm-cursor2.png) 3. Get your AI-generated answer based on NEAR documentation. ![VS Code](/assets/docs/tools/llm-cursor3.png) --- # Source: https://docs.near.org/protocol/network/validators.md --- id: validators title: Validators description: "Learn about NEAR Protocol validators, their roles in network security, consensus mechanisms, validator economics, and how to become a validator." --- At its core, NEAR Protocol is a decentralized blockchain that operates on a network of independent participants called validators who process transactions and secure the network. These validators must reach consensus, meaning they need to agree on which transactions are valid and should be added to the blockchain. This ensures no one steals funds, double-spend tokens, or manipulates the system. NEAR validators use [Proof-of-Stake (PoS)](https://en.wikipedia.org/wiki/Proof_of_stake), a consensus mechanism that secures the network through economic incentives rather than energy-intensive computational power. Unlike Bitcoin's [proof-of-work](https://en.wikipedia.org/wiki/Proof_of_work) system that requires massive amounts of electricity, PoS makes the network environmentally sustainable while maintaining robust security. In Proof-of-Stake, users demonstrate support for specific validators by delegating their NEAR tokens through a process called staking. The principle is straightforward: validators with more delegated tokens are trusted by the community to keep the network safe. If any of these validators is found doing a malicious activity they get kicked from the network and all the tokens staked on them are burned, making dishonesty economically irrational. ### Securing the Network Validators have two main jobs. The first is to validate and execute transactions, aggregating them in the blocks that form the blockchain. Their second job is to oversee other validators, making sure no one produces an invalid block or creates an alternative chain (eg. with the goal of creating a double spend). If a validator is caught misbehaving, then they get "slashed", meaning that their stake (or part of it) is burned. In the NEAR networks, an attempt to manipulate the chain would mean taking control over the majority of the validators at once, so that the malicious activity won't be flagged. However, this would require putting a huge sum of capital at risk, since an unsuccessful attack would mean slashing your staked tokens. ### Validator's Economy In exchange for servicing the network, validators are rewarded with a target number of NEAR every epoch. The target value is computed in such a way that, on an annualized basis, it will be 2.5% of the total supply. All transaction fees (minus the part which is allocated as the rebate for contracts) which are collected within each epoch are burned by the system. The inflationary reward is paid out to validators at the same rate regardless of the number of fees collected or burned. ## Intro to Validators [Validators](https://pages.near.org/papers/the-official-near-white-paper/#economics) are responsible for producing and validating blocks and chunks, ensuring the security and integrity of the NEAR network. The hardware requirements for running a validator node vary depending on the staking position. Detailed specifications can be found here: [the hardware requirements](https://near-nodes.io/validator/hardware-validator). You can view the list of currently active validators on platforms like [NEAR-STAKING](https://near-staking.com/stats). To become a validator, the minimum stake required is determined by the 300th largest staking proposal. If there are more than 300 proposals, the threshold will be set by the stake amount of the 300th proposal, as long as it exceeds the minimum threshold of 25,500 $NEAR. The current seat price to join the active validator set is updated in real-time on [NEAR BLOCKS](https://nearblocks.io/node-explorer). Any validator node with a stake greater than the current seat price can join the set of active validators.
Is there a plan to support GPU compute if certain validator nodes can offer that or is it just CPU?

We don't need GPU support as we are a POS chain and we require very little compute power. You can read more about our consensus strategy on our Validator Quickstart and Staking FAQ.
## Block & Chunk producers The top 100 validators are responsible for producing and validating blocks, as well as producing chunks. Under normal circumstances, each validator is assigned to a single shard, for which it produces chunks. Block & Chunk producers are guaranteed a minimum annual reward of 2.5%. If less than 100% of the network’s tokens are staked, validators have the potential to earn even higher annual rewards. ## Chunk Validators [Note] Block & Chunk producers also serve as chunk validators. Non-top 100 validators take on the role of chunk validators, which has lower hardware and staking requirements, making it more accessible. This role helps expand the network's validator set, increasing opportunities to earn rewards and strengthen the security of the NEAR ecosystem. Chunk validators do not track shards. Their responsibilities are focused solely on validating and endorsing chunks. Like block and chunk producers, chunk validators are guaranteed a minimum of 2.5% annual rewards. If less than 100% of the network’s tokens are staked, chunk validators may earn even higher rewards. For more details on validator economics, check out [NEAR’s Economics Explained](https://near.org/blog/near-protocol-economics/). ## Dedicated Validator Documentation Site If you'd like to further explore Validators and Nodes in general, you can visit the [Dedicated Validator Documentation Site](https://near-nodes.io/).
If a developer writes a vulnerable or malicious dApp, is a validator implicitly taking on risk?

No. We have handled the potential damages to the network on the protocol level. For example, we have a lot of limiters that constrain how much data you can pass into a function call or how much compute you can do in one function call, etc. That said, smart contract developers will need to be responsible for their own dApps, as there is no stage gate or approval process. All vulnerabilities can only damage the smart contract itself. Luckily, updating smart contracts is very smooth on NEAR, so vulnerabilities can be updated/patched to an account in ways that cannot be done on other blockchains.
--- # Source: https://docs.near.org/tutorials/multichain-dao/voting.md --- id: voting title: MultiSig Voting description: "Learn how to deploy a MultiSig contract and vote on multi-chain proposals using the Abstract DAO." --- Now that we understand how the Abstract DAO works, it is time to use it in within an organization. Lets see how to deploy a MultiSig contract, where users will vote on a multi-chain proposal. --- ## Creating a MultiSig Contract As a first step we need to create an account and deploy a MultiSig contract on it, so users can start creating proposals and voting on them. :::info Deploy Multisig You can download the [compiled multisig](https://github.com/near/core-contracts/raw/refs/heads/master/multisig2/res/multisig2.wasm) from the near repository ::: ```bash near create-account --useFaucet near deploy multisig2.wasm ``` :::tip See the github repository for instructions on how to [initialize the multisig contract](https://github.com/near/core-contracts/tree/master/multisig2) ::: --- ## Creating a Request on the Multisig Contract To call `register_signature_request` on the Multi-Chain DAO Governance Contract, you need to submit a request through your Multisig contract. This ensures that the decision to generate a signature is confirmed by the necessary members. ```bash near contract call-function as-transaction multisignature.testnet add_request json-args '{ "request": { "receiver_id": "abstract-dao.testnet", "actions": [ { "type": "FunctionCall", "method_name": "register_signature_request", "args": { "request": { "allowed_account_id": "executor.testnet", "derivation_seed_number": 0, "transaction_payload": { "to": "0xe2a01146FFfC8432497ae49A7a6cBa5B9Abd71A3", "nonce": "0", "function_data": { "function_abi": { "inputs": [ { "internalType": "uint256", "name": "_num", "type": "uint256" } ], "name": "set", "outputs": [], "stateMutability": "nonpayable", "type": "function" }, "arguments": [ { "Uint": "A97" } ] } } } }, "gas": "100000000000000", "deposit": "0.1" } ] } }' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as executor.testnet network-config testnet ``` --- ## Voting on the Request Once the request is submitted, members of the multisig contract have a set amount of time to vote to either Confirm or Reject the request. Each member needs to cast their vote using the following command: ```bash near contract call-function as-transaction multisignature.testnet confirm json-args '{"request_id": 1}' prepaid-gas '100.0 Tgas' attached-deposit '1 yoctoNEAR' sign-as account.testnet network-config testnet ``` :::note Replace provided `request_id` with value retrieved from the response when creating the request ::: Once the request has received enough confirmations, it will be automatically executed. At this point, the signature request is successfully registered on the Multi-Chain DAO Governance Contract. Now, the allowed account (specified in the request) can generate signatures for the transaction just as we saw in the [previous section](./2-signing.md). --- # Source: https://docs.near.org/web3-apps/tutorials/wallet-login.md --- id: wallet-login title: Wallet Login description: "Connect users to NEAR wallets with a secure, sandbox-based connector library" --- The `@hot-labs/near-connect` library provides a secure, zero-dependency wallet connector for NEAR blockchain with a unique sandbox-based architecture. ![Preview](https://github.com/user-attachments/assets/c4422057-38bb-4cd9-8bd0-568e29f46280) :::tip Example We have a [working example](https://github.com/near-examples/hello-near-connector), which you can easily get through `create-near-app`: ```bash npx create-near-app@latest ``` ::: --- ## Why NEAR Connect? Unlike traditional wallet selectors, it offers a dynamic manifest system that allows wallets to be added and updated without requiring developers to update their dependencies. - **Secure Execution**: Wallet scripts run in isolated sandboxed iframes for maximum security - **Dynamic Wallets**: Wallets are loaded from a manifest and can be updated without code changes - **Zero Dependencies**: Lightweight library with no external dependencies - **Automatic Detection**: Supports both injected wallets (extensions) and manifest-based wallets --- ## Installation You can add the NEAR Connect library to your project in two ways: Install the `@hot-labs/near-connect` and `near-api-js` packages manually: ```bash npm install @hot-labs/near-connect near-api-js ``` If you are using React, we recommend installing the `near-connect-hooks` package which provides convenient hooks for integrating NEAR Connect into your app: ```bash npm install near-connect-hooks ``` --- ## Initializing the Connector Initialize the `NearConnector` instance in your application: ``` const connector = new NearConnector({ manifest: process.env.NODE_ENV === "production" ? undefined : "/near-connect/repository/manifest.json", providers: { mainnet: ["https://relmn.aurora.dev"] }, walletConnect, network, logger, }); ``` ``` import { Navigation } from "@/components/navigation"; import { NearProvider } from "near-connect-hooks"; export default function App({ Component, pageProps }: AppProps) { return ( ); } ```
Selecting Wallets Unlike traditional wallet selectors that bundle wallet code, NEAR Connect uses a **manifest-based approach**: 1. Wallet providers register their integration scripts in a public manifest 2. The connector dynamically loads wallet scripts when users want to connect This architecture eliminates the need to install individual wallet packages and ensures wallet code can be updated independently from your app. ```tsx connector = new NearConnector({ network: "testnet", // or "mainnet" features: { signMessage: true, // Only show wallets that support message signing signTransaction: true, signInWithoutAddKey: true, signAndSendTransaction: true, signAndSendTransactions: true }, }); ```
--- ## Signing In / Out The connector uses the Observer Pattern (pub/sub), for which we need to subscribe to the `wallet:signIn" and `wallet:signOut` events ``` connector.on("wallet:signIn", async (t) => { setWallet(await connector.wallet()); setAccount(t.accounts[0]); }); connector.on("wallet:signOut", async () => { setWallet(undefined); setAccount(undefined); }); ``` Then, call the `connect` function to open a modal where the user can select a wallet and sign in, and `disconnect` to sign out ``` const connect = async () => { if (networkAccount != null) return connector.disconnect(); await connector.connect(); }; ``` The `near-connect-hooks` package provides a `useNearWallet` hook that simplifies the sign-in process, first import the hook: ``` import { useNearWallet } from 'near-connect-hooks'; ``` Then, import the `signIn` and `signOut` method from the hook and call them when needed: ``` const { signedAccountId, loading, signIn, signOut } = useNearWallet(); const handleAction = () => { if (signedAccountId) { signOut(); } else { signIn(); } }; ``` --- ## Calling Contract Method To call a contract method, first get the connected wallet instance using `connector.wallet()`, then use the wallet's `signAndSendTransaction` method to make a function call: ```tsx // Get the connected wallet const wallet = await connector.wallet(); // Call a change method const result = await wallet.signAndSendTransaction({ receiverId: "hello.near-examples.testnet", actions: [ { type: "FunctionCall", params: { methodName: "set_greeting", args: { greeting: "Hello from NEAR Connect!" }, gas: "30000000000000", // 30 TGas deposit: "0", // No deposit }, }, ], }); console.log("Transaction:", result.transaction.hash); ``` To call a contract method, you can use the `callFunction` method provided by the `useNearWallet` hook: ``` callFunction({ contractId: HelloNearContract, method: 'set_greeting', args: { greeting: newGreeting } }) .then(async () => { ``` --- ## Calling Read-only Methods The `near-connector` does not provide a built-in way to call read-only (view) methods. However, you can use the `near-api-js` package (or any of your preferred APIs) to create a JSON-RPC provider and call view methods directly: ```tsx import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); const greeting = await provider.callFunction( "hello.near-examples.testnet", "get_greeting", {} ); ``` You can use the `viewFunction` method provided by the `useNearWallet` hook to call read-only methods on the contract: ``` viewFunction({ contractId: HelloNearContract, method: 'get_greeting' }).then((greeting) => setGreeting(greeting as string)); }, [viewFunction]); ``` --- ## Get Balance The `near-connector` does not provide a built-in way to get the account balance. However, you can use the `near-api-js` package (or any of your preferred APIs) to create a JSON-RPC provider and query the account balance directly: ```tsx import { JsonRpcProvider } from "near-api-js"; const provider = new JsonRpcProvider({ url: "https://test.rpc.fastnear.com" }); const greeting = await provider.viewAccount({ accountId: "hello.near-examples.testnet" }); ``` You can use the `getBalance` method provided by the `useNearWallet` hook to call read-only methods on the contract: ```jsx import { useNearWallet } from 'near-connect-hooks'; import { yoctoToNear } from 'near-api-js'; const { getBalance } = useNearWallet(); const balance = await getBalance(); console.log(`Balance: ${yoctoToNear(balance.available, 2)} NEAR`); ``` --- ## Send Multiple Transactions You can request the user to sign and send multiple transactions in parallel through a single prompt: ```tsx const wallet = await connector.wallet(); const results = await wallet.signAndSendTransactions({ transactions: [ { receiverId: "token.near", actions: [ { type: "FunctionCall", params: { methodName: "ft_transfer", args: { receiver_id: "alice.near", amount: "1000000", }, gas: "30000000000000", deposit: "1", // 1 yoctoNEAR for security }, }, ], }, { receiverId: "nft.near", actions: [ { type: "FunctionCall", params: { methodName: "nft_mint", args: { token_id: "token-1", receiver_id: "alice.near", }, gas: "30000000000000", deposit: "10000000000000000000000", // 0.01 NEAR }, }, ], }, ], }); console.log(`Completed ${results.length} transactions`); ``` You can use the `signAndSendTransactions` method provided by the `useNearWallet` hook to send multiple transactions in a single request: ```tsx import { useNearWallet } from 'near-connect-hooks'; ... const { signAndSendTransactions } = useNearWallet(); const results = await signAndSendTransactions({ transactions: [ { receiverId: "token.near", actions: [ { type: "FunctionCall", params: { methodName: "ft_transfer", args: { receiver_id: "alice.near", amount: "1000000", }, gas: "30000000000000", deposit: "1", // 1 yoctoNEAR for security }, }, ], }, { receiverId: "nft.near", actions: [ { type: "FunctionCall", params: { methodName: "nft_mint", args: { token_id: "token-1", receiver_id: "alice.near", }, gas: "30000000000000", deposit: "10000000000000000000000", // 0.01 NEAR }, }, ], }, ], }); console.log(`Completed ${results.length} transactions`); ``` --- ### Sign Messages (NEP-413) In NEAR, users can sign messages for authentication purposes without needing to send a transaction: You can request the user to sign a message using the wallet's `signMessage` method: ```tsx const wallet = await connector.wallet(); const signature = await wallet.signMessage({ message: "Please sign this message to authenticate", recipient: "your-app.near", nonce: Buffer.from(crypto.randomUUID()), }); console.log("Signature:", signature.signature); console.log("Public Key:", signature.publicKey); // Verify the signature on your backend ``` You can use the `signMessage` method provided by the `useNearWallet` hook to request the user to sign a message: ```tsx import { useNearWallet } from 'near-connect-hooks'; ... const { signNEP413Message } = useNearWallet(); const signature = await signMessage({ message: "Please sign this message to authenticate", recipient: "your-app.near", nonce: Buffer.from(crypto.randomUUID()), }); console.log("Signature:", signature.signature); console.log("Public Key:", signature.publicKey); ``` --- # Source: https://docs.near.org/tools/wallet-selector.md # Source: https://docs.near.org/web3-apps/tutorials/web-login/wallet-selector.md # Source: https://docs.near.org/tools/wallet-selector.md # Source: https://docs.near.org/web3-apps/tutorials/web-login/wallet-selector.md # Source: https://docs.near.org/tools/wallet-selector.md # Source: https://docs.near.org/web3-apps/tutorials/web-login/wallet-selector.md # Source: https://docs.near.org/tools/wallet-selector.md # Source: https://docs.near.org/web3-apps/tutorials/web-login/wallet-selector.md # Source: https://docs.near.org/tools/wallet-selector.md # Source: https://docs.near.org/web3-apps/tutorials/web-login/wallet-selector.md --- id: wallet-selector title: Wallet Selector Tutorial description: "Enable users to sign-in using their NEAR wallet with the Wallet Selector" --- Allowing your users to connect with their favorite wallet and interact with your dApp is a crucial step in building web3 applications. This guide will help you to integrate the `Wallet Selector` into your frontend, enabling users to sign-in and perform transactions using their NEAR wallet. ![Preview](/assets/docs/tools/wallet-selector-preview.png) :::tip To see a fully working example, check our [Hello NEAR Example](https://github.com/near-examples/hello-near-examples/frontend), a simple app that allows users to set and get a greeting message on the NEAR blockchain ::: :::info Check other options to let users login into your application and use NEAR accounts in the [Web Login](../../concepts/web-login.md) section ::: --- ## Adding the Wallet Selector To start, you will need to add the `Wallet Selector` and its dependencies to your project. If you prefer to explore the complete code example, you can check the [hello-near-example](https://github.com/near-examples/hello-near-examples/tree/main/frontend) repository: ```bash npm install \ @near-wallet-selector/modal-ui \ @near-wallet-selector/bitget-wallet \ @near-wallet-selector/coin98-wallet \ @near-wallet-selector/ethereum-wallets \ @near-wallet-selector/hot-wallet \ @near-wallet-selector/intear-wallet \ @near-wallet-selector/ledger \ @near-wallet-selector/math-wallet \ @near-wallet-selector/meteor-wallet \ @near-wallet-selector/meteor-wallet-app \ @near-wallet-selector/near-mobile-wallet \ @near-wallet-selector/okx-wallet \ @near-wallet-selector/ramper-wallet \ @near-wallet-selector/sender \ @near-wallet-selector/unity-wallet \ @near-wallet-selector/welldone-wallet ``` Notice that the wallet selector implements multiple wallet packages to select from, [see the full list on the Repo](https://github.com/near/wallet-selector) --- ## Initialize the Selector To initialize the wallet selector, you will need to set it up in your main application file (e.g., `app.js` or `index.js`). You can choose which wallets to include in the selector by importing their respective setup functions and adding them to the `modules` array. ```jsx title="app.js" const walletSelectorConfig = { network: "testnet", // "mainnet" // Optional: createAccessKeyFor: "hello.near-examples.testnet", modules: [ setupBitteWallet(), setupMeteorWallet(), setupLedger(), setupNightly() ], } export default function App({ Component }) { return ( ); } ```
### Custom RPC If you want to use a user-defined RPC endpoint with the Wallet Selector, you can set up a [network options](https://github.com/near/wallet-selector/tree/main/packages/core#options) object with the custom URLs. For example: ```js const my_network = { networkId: "my-custom-network", nodeUrl: "https://rpc.custom-rpc.com", helperUrl: "https://helper.custom-helper.com", explorerUrl: "https://custom-explorer.com", indexerUrl: "https://api.custom-indexer.com", }; ```
### Creating an Access Key If you instantiated the `wallet-selector` passing an account id for the `createAccessKeyFor` parameter, then the wallet will create a [Function-Call Key](/protocol/access-keys#function-call-keys) and store it in the web's local storage. ```js const walletSelectorConfig = { network: "testnet", // "mainnet" createAccessKeyFor: "hello.near-examples.testnet", modules: [ setupMeteorWallet() // ... ], } ``` By default, such key enables to expend a maximum of `0.25Ⓝ` on GAS calling methods in **the specified** contract **without prompting** the user to sign them. If, on the contrary, you do not create an access key, then the user will be asked to sign every single transaction (except calls to `view methods`, since those are always free). :::tip Please notice that this only applies to **non-payable** methods, if you attach deposit to any call the user will **always** be redirected to the wallet to confirm the transaction. ::: --- ## Calling View Methods Once the wallet-selector is up, we can start calling view methods, i.e., the methods that perform read-only operations. Because of their read-only nature, view methods are **free** to call, and do **not require** the user to be **logged in**. To make a view call, you simply need to import the `viewFunction` from the wallet selector hooks: ``` const { signedAccountId, viewFunction, callFunction } = useWalletSelector(); ``` Then, you can call any view method from any contract by providing the `contractId`, `methodName`, and arguments (`args`): ``` useEffect(() => { viewFunction({ contractId: HelloNearContract, method: 'get_greeting' }).then((greeting) => setGreeting(greeting as string)); }, []); ``` Under the hood, `viewFunction` is actually making a **direct call to the RPC** using `near-api-js`. :::tip View methods have by default 200 TGAS for execution ::: --- ## User Sign-In / Sign-Out In order to interact with methods that modify data or make cross-contract calls it is necessary for the user to first sign in using a NEAR wallet. We can request the user sign in if `signedAccountId` is not present, the same simplicity applies to signing out. ``` const { signedAccountId, signIn, signOut } = useWalletSelector(); useEffect(() => { if (signedAccountId) { setAction(() => signOut); setLabel(`Logout ${signedAccountId}`); } else { setAction(() => signIn); setLabel('Login'); } }, [signedAccountId, signIn, signOut]); ``` By assigning the `signIn` action to a button, when the user clicks it, the wallet selector modal will open: ```jsx ``` --- ## Calling Change Methods Once the user logs in they can start calling `change methods`. Programmatically, calling `change methods` is similar to calling `view methods`, only that now you can attach deposit to the call, and specify how much GAS you want to use. ``` const saveGreeting = async () => { // Try to store greeting, revert if it fails callFunction({ contractId: HelloNearContract, method: 'set_greeting', args: { greeting: newGreeting } }) .then(async () => { const greeting = (await viewFunction({ contractId: HelloNearContract, method: 'get_greeting' })) as string; setGreeting(greeting); setShowSpinner(false); }); // Assume the transaction will be successful and update the UI optimistically setShowSpinner(true); setGreeting(newGreeting); }; return ( ``` Under the hood, we are asking the **signedAccountId** to **sign a Function-Call transaction** for us. :::tip Remember that you can use the `callFunction` to call methods in **any** contract. If you did not ask for a function call key to be created, the user will simply be prompted to confirm the transaction. :::
## Sending Multiple Transactions The wallet-selector hook also exposes a method that can be used to send multiple transactions at once: ```js const { signAndSendTransactions } = useWalletSelector(); const txs = await signAndSendTransactions({ transactions: [{ receiverId: "hello.near-examples.testnet", actions: [{ type: "FunctionCall", params: { methodName: "set_greeting", args: { greeting: "Hello World" }, gas: THIRTY_TGAS, deposit: NO_DEPOSIT } }] }] }); ``` Transactions can either be sent as multiple separate transactions simultaneously or as a batch transaction made up of actions where if one of the actions fails, they are all reverted. An example of both can be seen [here](../../../tutorials/examples/frontend-multiple-contracts#dispatching-multiple-transactions). --- ## Querying Account Balance By calling the `getBalance` method the user can get the balance of a given account. ```js const { getBalance } = useWalletSelector(); const balance = await getBalance("account.testnet"); ``` --- ## Get Access Keys The final method the wallet selector hooks exposes is `getAccessKeys`, which is used to return an object of all the access keys on the account that is currently logged in. ```js const { getAccessKeys } = useWalletSelector(); const keys = await getAccessKeys("account.testnet"); ``` ::: :::note Versioning for this article At the time of this writing, this example works with the following versions: - next: `15.0.3` - near-api-js: `^5.0.1` - wllet-selector/core: `^8.10.0` ::: --- # Source: https://docs.near.org/web3-apps/concepts/web-login.md --- title: Web Login Methods id: web-login description: "Learn all the login options available for your website or web app" --- NEAR offers multiple options to allow users to login using NEAR accounts. Below are the most popular methods to integrate web login into your web app or website, each tailored to different use cases and user experiences. Once the user is logged in, they will be able to use their accounts to interact with the NEAR blockchain, making call to smart contracts, transfer tokens, and more. --- ## NEAR Connector [NEAR Connect](https://github.com/azbang/near-connect) is a zero-dependency lightweight library that allows users to connect to your dApp using their preferred wallet. It uses a secure sandbox-based architecture where wallet scripts run in isolated iframes. ![Preview](https://github.com/user-attachments/assets/c4422057-38bb-4cd9-8bd0-568e29f46280) :::tip You can learn how to integrate NEAR Connect into your app in the [NEAR Connector tutorial](../tutorials/wallet-login.md) ::: --- ## Social Login [Privy](https://www.privy.io/) is a third-party service that allows users to login using their email or social accounts (Google, Facebook, Twitter, etc). Upon login, a NEAR wallet is created for the user, which they can fund and use to interact with your dApp. :::tip Check our [Privy Integration Example](https://github.com/near-examples/hello-privy/) to learn how to integrate Privy into your web app ::: ![Preview](https://framerusercontent.com/images/ugUCPrqIGlKFdxBwSbRoWriZtE.png?scale-down-to=2048&width=4018&height=2262) :::info Web3Auth For an alternative social login method you can check [Web3Auth](https://web3auth.io/). We have a functional [Web3Auth Integration Example](https://github.com/near-examples/hello-web3auth/) to show how to integrate Web3Auth into your web app. ::: --- # Source: https://docs.near.org/web3-apps/tutorials/web-login/web3-auth.md --- id: web3-auth title: Web3Auth Social Login Integration sidebar_label: Web3Auth Integration --- # Integrating Web3Auth with NEAR This tutorial demonstrates how to integrate [Web3Auth](https://web3auth.io/) into a NEAR application, enabling users to log in with social accounts (Google, Facebook, Twitter, etc.) while maintaining full blockchain functionality. :::tip What is Web3Auth? Web3Auth is a pluggable authentication infrastructure that allows users to authenticate using familiar Web2 logins (OAuth providers like Google, Facebook, etc.) while generating a cryptographic key pair for Web3 interactions. This provides a seamless onboarding experience without requiring users to manage seed phrases or private keys directly. ::: ## Clone and Install Start by cloning the repository and installing dependencies: ```bash git clone https://github.com/near-examples/hello-web3auth.git cd hello-web3auth/modal yarn install ``` --- ## Get Web3Auth Credentials To enable social login, you need to create a Web3Auth project and obtain a Client ID. ### Create a Web3Auth Account 1. Go to [Web3Auth Dashboard](https://dashboard.web3auth.io/) 2. Sign up or log in with your preferred method 3. You'll be redirected to the dashboard ### Create a New Project 1. Click on **"Add new project"** 2. Fill in the project details: - **Project Name**: Choose a descriptive name (e.g., "NEAR Social Login") - **Environment**: Select **"Sapphire Devnet"** for development - **Chain Namespace**: Choose **"Other"** (since NEAR is not natively supported) 3. Click **"Create"** ### Configure Your Project 1. Once created, click on your project to open its settings.In this section you can find Client ID and Client Secret which will be used in the application configuration. 2. Change the **Select Product** to **MPC Core Kit** 3. Change **Project platform** to **Web Application** 4. Go to the **"Domains"** tab 5. Add your application's URLs: - **Whitelist URLs**: Add `http://localhost:5173` for local development - For production, add your deployed URL (e.g., `https://yourdomain.com`) --- ## Configure Google OAuth (Optional but Recommended) While Web3Auth provides default OAuth providers, setting up your own Google OAuth gives you more control and branding. ### Create a Google Cloud Project 1. Open the [Google Cloud Console](https://console.cloud.google.com/). 2. Click Select a project → New Project (or choose an existing project). 3. Enter a project name (for example, NEAR Web3Auth) and click Create. 4. In the left sidebar, go to **APIs & Services** → **OAuth consent screen** ([direct link](https://console.cloud.google.com/auth/overview/create)). ### Configure OAuth Consent Screen 1. Go to **"APIs & Services"** → **"OAuth consent screen"** 2. Choose **"External"** (unless you have a Google Workspace) 3. Fill in the required information: - **App name**: Your application name - **User support email**: Your email - **Developer contact**: Your email 4. Click **"Save and Continue"** 5. On the **Scopes** page, click **"Save and Continue"** (default scopes are fine) 6. On the **Test users** page, add your email for testing 7. Click **"Save and Continue"** ### Create OAuth Credentials 1. Go to **"APIs & Services"** → **"Credentials"** 2. Click **"+ Create Credentials"** → **"OAuth client ID"** ([direct link](https://console.cloud.google.com/auth/clients/create)) 3. Select **"Web application"** 4. Configure: - **Name**: Choose a descriptive name - **Authorized JavaScript origins**: - Add `http://localhost:5173` (for development) - Add your production domain (when ready) - **Authorized redirect URIs**: - Add `https://auth.web3auth.io/auth` - This is Web3Auth's callback URL 5. Click **"Create"** 6. Copy the **Client ID** and **Client Secret** ### Add Google Credentials to Web3Auth 1. Return to [Web3Auth Dashboard](https://dashboard.web3auth.io/) 2. Go to the **Authentication** in left sidebar 3. Click on **Google** in social Logins 4. Click on **Add connection** and fill in the following details: - **Auth Connection ID***: Your desired connection ID (e.g., `near-login`),This use for verifier value in application configuration. - **Enter Google Client ID***: Your Google OAuth Client Secret from previous step.(e.g., `17426988624-32m2gh1o1n5qve6govq04ue91sioruk7WWapps.googleusercontent.com`) --- ## Configure Your Application Now that you have your Web3Auth Client ID, update your application configuration. ### Update the Config File Open `src/config.js` and replace the `clientId` with your own: ``` // Web3Auth Config import { CHAIN_NAMESPACES, WEB3AUTH_NETWORK } from '@web3auth/base' import { CommonPrivateKeyProvider } from '@web3auth/base-provider' const chainConfig = { chainNamespace: CHAIN_NAMESPACES.OTHER, chainId: 'near:testnet', // NEAR mainnet rpcTarget: 'https://rpc.testnet.near.org', displayName: 'NEAR Mainnet', blockExplorerUrl: 'https://nearblocks.io', ticker: 'NEAR', tickerName: 'NEAR', } const privateKeyProvider = new CommonPrivateKeyProvider({ config: { chainConfig } }) export const web3AuthContextConfig = { web3AuthOptions: { clientId: 'BP1rATmBxrPQ5BK0cMry4vmcOXJwYVSElff0dnb3in004j9lFE2SI2QUlC9Sy9lkqVgzussY6QPkOXWocnoJGGI', web3AuthNetwork: WEB3AUTH_NETWORK.SAPPHIRE_DEVNET, chainConfig, privateKeyProvider, } } // NEAR Config const contractPerNetwork = { mainnet: 'hello.near-examples.near', testnet: 'hello.near-examples.testnet', } export const NetworkId = 'testnet' export const providerUrl = 'https://test.rpc.fastnear.com' export const HelloNearContract = contractPerNetwork[NetworkId] ``` --- ## Run the Application Start the development server: ```bash yarn run dev ``` Open your browser and navigate to `http://localhost:5173`. ### Testing the Integration 1. Click the **"Login"** button in the navigation bar 2. The Web3Auth modal will appear with various login options 3. Choose **"Continue with Google"** (or another provider) 4. Complete the authentication flow 5. Once logged in, you'll see: - Your derived NEAR account ID in the navigation - Your NEAR balance - A logout button with your email/name --- ## Understanding the Implementation ### Architecture Overview This integration uses two main components: 1. **Web3Auth Modal** (`@web3auth/modal-react-hooks`): Provides the UI and authentication flow 2. **NEAR Integration** (custom provider): Derives NEAR keys from Web3Auth and manages blockchain interactions ### Key Components #### 1. Web3Auth Provider (`App.jsx`) ``` import { NEARxWeb3Auth } from './context/provider' import { web3AuthContextConfig } from './config'; function App() { // Use HashRouter when deployed under a subpath (GitHub Pages) const useHashRouter = import.meta.env.BASE_URL !== '/' const Router = useHashRouter ? HashRouter : BrowserRouter return ( } /> } /> ) } export default App ``` The `Web3AuthProvider` wraps your application and provides authentication state. #### 2. NEAR Context Provider (`src/context/provider.jsx`) This custom provider bridges Web3Auth and NEAR: ``` // near api js import { JsonRpcProvider } from '@near-js/providers' import { Account } from '@near-js/accounts' // utils import { base58 } from '@scure/base' // config import { useEffect } from 'react' import { useState } from 'react' import { NearContext } from './useNear' import { providerUrl } from '../config' import { useWeb3Auth } from '@web3auth/modal-react-hooks' import { KeyPair } from '@near-js/crypto' import { KeyPairSigner } from '@near-js/signers' import { getED25519Key } from '@web3auth/base-provider' // Provider const provider = new JsonRpcProvider({ url: providerUrl }) // eslint-disable-next-line react/prop-types export function NEARxWeb3Auth({ children }) { const [walletId, setWalletId] = useState(null) const [nearAccount, setNearAccount] = useState(null) const [loading, setLoading] = useState(true) // Get Web3Auth Modal state - this provides the authenticated session const { isConnected, authenticateUser, userInfo, provider: web3authProvider } = useWeb3Auth() // Sync MPC Core Kit with Web3Auth Modal authentication useEffect(() => { const syncAuth = async () => { if (!isConnected) { setWalletId(null) setNearAccount(null) setLoading(false) return } if (!web3authProvider) return; const privateKey = await web3authProvider.request({ method: 'private_key' }) const privateKeyEd25519 = getED25519Key(privateKey).sk // Already a Uint8Array // Create keypair and derive account ID from public key const keyPair = KeyPair.fromString(`ed25519:${base58.encode(privateKeyEd25519)}`); const signer = new KeyPairSigner(keyPair); const accountId = Array.from(keyPair.getPublicKey().data).map(b => b.toString(16).padStart(2, '0')).join('') const account = new Account(accountId, provider, signer); setWalletId(account.accountId) setNearAccount(account) setLoading(false) } syncAuth() }, [isConnected, authenticateUser, userInfo, web3authProvider]) return ( {children} ) } ``` #### 3. Using the Context (`src/components/navigation.jsx`) Components can access both Web3Auth and NEAR state: ``` import { useEffect, useState } from 'react' import NearLogo from '@/assets/near-logo.svg' import { Link } from 'react-router' import styles from '@/styles/app.module.css' import { useNEARxWeb3Auth } from '../context/useNear' import { useWeb3Auth } from '@web3auth/modal-react-hooks' import { NEAR } from '@near-js/tokens' export const Navigation = () => { const [action, setAction] = useState(() => {}) const [label, setLabel] = useState('Loading...') const [balance, setBalance] = useState(0) // Web3Auth Modal for easy login UI const { connect, logout, isConnected, userInfo } = useWeb3Auth() // MPC Core Kit for NEAR transaction signing const { walletId, nearAccount, loading } = useNEARxWeb3Auth() useEffect(() => { if (loading) { setLabel('Loading...') return } if (isConnected) { const userId = userInfo?.email || userInfo?.name || 'User' setAction(() => logout) setLabel(`Logout ${userId}`) } else { setAction(() => connect) setLabel('Login') setBalance(null) } }, [isConnected, walletId, loading, userInfo, logout, connect]) useEffect(() => { if (walletId && nearAccount) { nearAccount .getBalance() .then((b) => setBalance(Number(NEAR.toDecimal(b)).toFixed(2))) .catch(() => setBalance(0)) // non existing account } }, [walletId, nearAccount]) ``` ### How It Works 1. **User Authentication**: User logs in via Web3Auth (Google, etc.) 2. **Key Derivation**: Web3Auth generates a private key based on the user's social login 3. **NEAR Key Conversion**: The private key is converted to NEAR's ED25519 format 4. **Account ID Generation**: A deterministic account ID is derived from the public key 5. **Blockchain Interaction**: The NEAR account instance can now sign transactions :::warning NEAR Provider Implementation After a user logs in, they receive a provider from the Embedded Wallets SDK. However, there is no native provider for NEAR, so we use the private key to make RPC calls directly. This is why we extract the private key from Web3Auth's provider and create a custom NEAR account instance using `@near-js/accounts` and `@near-js/providers`. ::: --- ## Interacting with Smart Contracts Once authenticated, you can interact with NEAR smart contracts. Here's an example from `src/pages/hello_near.jsx`: ``` import { useEffect, useState } from 'react' import { Cards } from '@/components/cards' import styles from '@/styles/app.module.css' import { HelloNearContract } from '@/config' import { useNEARxWeb3Auth } from '../context/useNear' import { useCallback } from 'react' // Contract that the app will interact with const CONTRACT = HelloNearContract export default function HelloNear() { const [greeting, setGreeting] = useState('loading...') const [newGreeting, setNewGreeting] = useState('loading...') const [loggedIn, setLoggedIn] = useState(false) const [showSpinner, setShowSpinner] = useState(false) const { provider, nearAccount, walletId } = useNEARxWeb3Auth() const fetchGreeting = useCallback(async () => { const greeting = await provider.callFunction(CONTRACT, 'get_greeting', {}) setGreeting(greeting) }, [provider]) const saveGreeting = async () => { nearAccount .callFunction({ contractId: CONTRACT, methodName: 'set_greeting', args: { greeting: newGreeting }, }) .catch((e) => { alert( `Error, did you deposit any NEAR Ⓝ? You can get some at https://dev.near.org/faucet` ) console.log(`Error saving greeting: ${e.message}`) fetchGreeting() }) setShowSpinner(true) await new Promise((resolve) => setTimeout(resolve, 300)) setGreeting(newGreeting) setShowSpinner(false) } useEffect(() => { setLoggedIn(!!walletId) }, [walletId]) useEffect(() => { fetchGreeting() }, [fetchGreeting]) ``` --- --- # Source: https://docs.near.org/smart-contracts/security/welcome.md --- id: welcome title: Security description: "Learn about smart contract security best practices on NEAR, including common vulnerabilities, attack vectors, and how to build secure decentralized applications." --- Here you will find information on how to keep your smart contract and decentralized applications secure. :::tip Please take some time to go through this section, it will help you to keep your dApp safe ::: --- ## 🐞 Bug Bounty Program NEAR has a [bug bounty program](https://hackenproof.com/company/near/programs) . Please use it if you find any security errors in the protocol, core contracts or web. Hackers - help audit, test, and toughen NEAR up! ## ✅ Security Checklist Make sure to keep your smart contract safe by running through our [security checklist](checklist.md). ## 🛡️ Security Concepts Learn important security concepts to keep your dApp safe and sound. Read about: - Keeping [callbacks safe](./callbacks.md) - Understanding [frontrunning](./frontrunning.md) - Understanding [sybil attacks](./sybil.md) - Understanding [reentrancy attacks](./reentrancy.md) - Making sure the owner of an account is [the one calling](./one_yocto.md) - Understanding the complexity of [generating random numbers](./random.md) - Protecting from a [small deposit attacks](./storage.md) draining your account ## 🎞️ External Resources An awesome video series on Smart Contract Security by [https://twitter.com/timurguvenkaya](https://twitter.com/timurguvenkaya) can be found on YouTube: --- # Source: https://docs.near.org/ai/shade-agents/concepts/what-can-you-build.md --- id: what-can-you-build title: What can you Build? sidebar_label: What can you Build? description: "Explore the features of Shade Agents and what they enable you to build, including Agentic Protocols, autonomous yield optimizers, and verifiable oracles." --- With their extensive list of features, Shade Agents unlock a wide range of new use cases, enable many previously centralized apps to become decentralized, and change how blockchain applications are designed. ## Shade Agent Features Shade Agents are: - **Non-custodial** - No one entity controls the private keys of the agent (MPC signing). - **Verifiable** - Agent code is known and executes as expected; no one can tamper with the agent. - **Multichain** - Can hold any crypto asset and sign transactions on any chain. - **AI Powered** - Can verifiably access LLMs. - **Confidential** - Inputs and state of the agent can be private. - **Flexible** - Interact with off-chain data and APIs beyond blockchain VM limits. - **Low cost** - Run intensive computation cheaply without gas limits (~20% TEE overhead compared to standard servers). --- ## Agentic Protocols Shade Agents enable a new paradigm of decentralized applications where a component of the logic for a protocol is run in a verifiable off-chain environment. This allows for more flexibility in terms of computation, cost, privacy, and the data you have access to. Agentic Protocols are designed to be autonomous, proactive, and intelligent. - A Yield Optimizer that earns optimal yield on assets across various DeFi protocols on different chains and autonomously rebalances according to a verifiable algorithm to get users the best rate, always. - A staking protocol that can implement complex reward mechanics. - A private DAO agent on Ethereum Mainnet that takes votes confidentially and publishes the end result in a single transaction. - A smart contract that can verifiably book users' plane tickets by calling the contract with funds and passing secrets (passport information and email address) to the agent directly. --- ## Flexible Oracles Since Shade Agents can access off-chain data and APIs, they make great cheap, flexible, quick-to-spin-up oracles. - A custom data oracle where app developers list a number of different sources, a consensus mechanism (4 of 5 sources state the same data), and the frequency with which data is pushed (or on demand), which allows smart contracts to access any data verifiably. - A prediction market resolver agent that uses an LLM to interpret multiple webpages and APIs to resolve markets quickly and accurately. - Verifiable randomness oracle. --- ## Smart Contracts using LLMs Shade Agents allow you to access LLMs from a smart contract that lives on any chain. The LLM and agent are both running in a TEE, so one can verify that the response from the agent is actually from an LLM and its response is a function of the provided inputs. There are two main flows: 1) The smart contract emits an event during a function call, the agent indexes this, uses the LLM to make a decision, and the agent calls a function on the contract with the result that is restricted to the agent's address. 2) Users interact with the agent which makes decisions via the LLM and carries out certain on-chain actions based upon the decision (transfers, function calls, swaps, etc.). With this, you could create: - A DAO with an AI President that makes decisions based on aggregate user opinions, historical context, and user reputation formulated from on-chain and off-chain data. - A decentralized social platform that uses an LLM to moderate content. - A treasury that trades assets according to news, sentiment, and historical price. --- ## Custom Authentication Shade agents allow users to control accounts through ways other than a private key and have it be non-custodial. - A prediction market that lets you create new markets and place bets all from Twitter (X). - A more secure social onboarding experience where you can log in via Google, Email, etc. - A Telegram bot for trading within a group chat. --- # Source: https://docs.near.org/data-infrastructure/what-is.md # Source: https://docs.near.org/primitives/what-is.md # Source: https://docs.near.org/web3-apps/what-is.md # Source: https://docs.near.org/smart-contracts/what-is.md # Source: https://docs.near.org/chain-abstraction/what-is.md --- id: what-is title: What is Chain Abstraction? sidebar_label: Introduction description: "Learn how NEAR allows you to seamlessly work across all chains" --- Through a combination of innovative technologies, NEAR enables developers to build applications that work seamlessly across multiple blockchains while abstracting away the underlying complexity for both developers and end users. --- ## Multi-Chain Accounts Chain Signatures allow NEAR accounts **and smart contracts** to sign transactions for all other chains (including Bitcoin, Ethereum and Solana) [Learn More](./chain-signatures.md) ## Swaps via Intents A decentralized system where users simply **express desired outcomes** (like "swap BTC for ETH at the best price"), and a network of solvers then competes to fulfill these intents optimally [Learn More](./intents/overview.md) ## OmniBridge A multi-chain bridge that enables secure and efficient cross-chain transfers. The bridge serves as both a token factory and custodian, managing native and bridged tokens through a unified interface [Learn More](./omnibridge/overview.md) --- ## Why Chain Abstraction Matters By building on NEAR, developers do not need to worry about the complexities of integrating with multiple blockchains. Instead, they can focus on building great applications that work seamlessly across all chains. Meanwhile, users can enjoy a smooth experience, using unified accounts and assets without needing to even understand on which blockchain they are operating.
Example: Cross-Chain NFT Marketplace Imagine building a digital art marketplace where users can purchase NFTs from different blockchains (Ethereum, Solana, etc.). Without chain abstraction, you'd need to: - Implement multiple blockchain connections - Handle different wallet types - Manage cross-chain transfers - Build complex UIs to explain blockchain concepts With chain abstraction, both you and your users just focus on the core experience: browsing and trading art. All blockchain complexity is handled automatically behind the scenes.
--- --- # Source: https://docs.near.org/tutorials/auction/winning-an-nft.md --- id: winning-an-nft title: Winning an NFT description: "Lets make the auction winner get an NFT." --- No one will enter an auction if there's nothing to win, so let's add a prize. Why not an [NFT](../../primitives/nft/nft.md)? NFTs are uniquely identifiable, easily swappable and their logic comes from an external contract so the prize will exist without the auction contract. Let's get to work! --- ## Listing the NFT When we create an auction we need to list the NFT. To specify which NFT is being auctioned off we need the account ID of the NFT contract and the token ID of the NFT. We will specify these when the contract is initialized; amend `init` to add `nft_contract` and `token_id` as such: ``` init({ end_time, auctioneer, nft_contract, token_id }: { end_time: bigint, auctioneer: AccountId, nft_contract: AccountId, token_id: string }) { this.auction_end_time = end_time; this.highest_bid = { bidder: near.currentAccountId(), bid: BigInt(1) }; this.auctioneer = auctioneer; this.nft_contract = nft_contract; this.token_id = token_id; } ``` ``` pub fn init( end_time: U64, auctioneer: AccountId, nft_contract: AccountId, token_id: TokenId, ) -> Self { Self { highest_bid: Bid { bidder: env::current_account_id(), bid: NearToken::from_yoctonear(1), }, auction_end_time: end_time, auctioneer, claimed: false, nft_contract, token_id, } } ``` Note that `token_id` is of type `TokenId` which is a String type alias that the NFT standards use for future-proofing. --- ## Transferring the NFT to the winner When the method `claim` is called the NFT needs to be transferred to the highest bidder. Operations regarding NFTs live on the NFT contract, so we make a cross-contract call to the NFT contract telling it to swap the owner of the NFT to the highest bidder. The method on the NFT contract to do this is `nft_transfer`. ``` return NearPromise.new(this.nft_contract) .functionCall("nft_transfer", JSON.stringify({ receiver_id: this.highest_bid.bidder, token_id: this.token_id }), BigInt(1), TWENTY_TGAS) .then(NearPromise.new(this.auctioneer).transfer(this.highest_bid.bid)) } ``` In near-sdk-js we cannot transfer the NFT and send the $NEAR independently so we will chain the promises. We will create a new file in our source folder named `ext.rs`; here we are going to define the interface for the `nft_transfer` method. We define this interface as a `trait` and use the `ext_contract` macro to convert the NFT trait into a module with the method `nft_transfer`. Defining external methods in a separate file helps improve the readability of our code. We then use this method in our `lib.rs` file to transfer the NFT. ``` let transfer_nft = nft_contract::ext(self.nft_contract.clone()) .with_static_gas(Gas::from_tgas(30)) .with_attached_deposit(NearToken::from_yoctonear(1)) .nft_transfer(self.highest_bid.bidder.clone(), self.token_id.clone()); ``` ``` use near_sdk::{ext_contract, AccountId}; use crate::TokenId; // NFT interface for cross-contract calls #[ext_contract(nft_contract)] #[allow(dead_code)] trait NFT { fn nft_transfer(&self, receiver_id: AccountId, token_id: TokenId); } ``` When calling this method we specify the NFT contract name, that we are attaching 30 Tgas to the call, that we are attaching a deposit of 1 YoctoNEAR to the call, and give the arguments `receiver_id` and `token_id`. The NFT requires that we attach 1 YoctoNEAR for [security reasons](../../smart-contracts/security/one_yocto.md). --- ## NFT ownership problems In our contract, we perform no checks to verify whether the contract actually owns the specified NFT. A bad actor could set up an auction where the NFT being auctioned doesn't belong to the auction contract, causing `nft_transfer` to fail and the winning bidder to lose their bid funds with nothing in return. We could make a cross-contract call to the NFT contract to verify ownership on initialization but this would become quite complex. Instead, we will do this check off-chain and validate the auction in the frontend. --- ## Displaying the contract object Since we are now dealing with more information in our contract, instead of implementing a function to display each field we'll create a function to display the entire contract object. Since the contract doesn't include large complex data structures like a map displaying the contract state in its entirety is easily done. ``` @view({}) get_auction_info(): AuctionContract { return this; } } ``` ``` #[near(contract_state, serializers = [json, borsh])] #[derive(PanicOnDefault)] pub struct Contract { highest_bid: Bid, auction_end_time: U64, auctioneer: AccountId, claimed: bool, nft_contract: AccountId, token_id: TokenId, } ``` We add the `serilizers` macro to enable json serialization so the object as a whole can easily be displayed to the frontend without having to output each field individually. ``` self.auction_end_time } pub fn get_auction_info(&self) -> &Contract { ``` ## Testing with multiple contracts In our tests, we're now going to be using two contracts; the auction contract and an NFT contract. Sandbox testing is great as it allows us to test multiple contracts in a realistic environment. In our tests folder, we need the WASM for an NFT contract. For this tutorial, we compiled an example NFT contract from [this repo](https://github.com/near-examples/NFT/tree/master). To deploy the NFT contract, this time we're going to use `dev deploy` which creates an account with a random ID and deploys the contract to it by specifying the path to the WASM file. After deploying we will initialize the contract with default metadata and specify an account ID which will be the owner of the NFT contract (though the owner of the NFT contract is irrelevant in this example). Default metadata sets information such as the name and symbol of the NFT contract to default values. ``` const nft_contract = await root.devDeploy(NFT_WASM_FILEPATH); await nft_contract.call(nft_contract, "new_default_meta", { "owner_id": nft_contract.accountId }); ``` ``` let auctioneer = create_subaccount(&sandbox, "auctioneer.sandbox").await?; let nft_contract = create_subaccount(&sandbox, "nft-contract.sandbox") .await? .as_contract(); let contract = create_subaccount(&sandbox, "contract.sandbox") .await? .as_contract(); // Initialize signer for the contract deployment let signer = near_api::Signer::from_secret_key( near_sandbox::config::DEFAULT_GENESIS_ACCOUNT_PRIVATE_KEY ``` --- ## Minting an NFT To start a proper auction the auction contract should own an NFT. To do this the auction account calls the NFT contract to mint a new NFT providing information such as the image for the NFT. ``` const TOKEN_ID = "1"; let request_payload = { "token_id": TOKEN_ID, "receiver_id": contract.accountId, "metadata": { "title": "LEEROYYYMMMJENKINSSS", "description": "Alright time's up, let's do this.", "media": "https://external-content.duckduckgo.com/iu/?u=https%3A%2F%2Ftse3.mm.bing.net%2Fth%3Fid%3DOIP.Fhp4lHufCdTzTeGCAblOdgHaF7%26pid%3DApi&f=1" }, }; await nft_contract.call(nft_contract, "nft_mint", request_payload, { attachedDeposit: NEAR.from("8000000000000000000000").toString(), gas: "300000000000000" }); ``` ``` .unwrap(), )?; // Deploy the NFT contract near_api::Contract::deploy(nft_contract.account_id().clone()) .use_code(nft_wasm) .with_init_call( "new_default_meta", json!({"owner_id": nft_contract.account_id()}), )? .with_signer(signer.clone()) .send_to(&sandbox_network) .await? .assert_success(); // Mint NFT const TOKEN_ID: &str = "1"; let request_payload = json!({ "token_id": TOKEN_ID, "receiver_id": contract.account_id(), ``` --- ## Verifying ownership of an NFT After `claim` is called, the test should verify that the auction winner now owns the NFT. This is done by calling `nft_token` on the NFT contract and specifying the token ID which will return the account ID that the token belongs to. ``` const response = await nft_contract.call(nft_contract, "nft_token",{"token_id": "1"},{ gas: "300000000000000" }); t.is(response.owner_id,bob.accountId); ``` ``` contract .call_function("bid", ()) .transaction() .deposit(NearToken::from_near(1)) .with_signer(alice.account_id().clone(), signer.clone()) .send_to(&sandbox_network) .await? .assert_failure(); // Auctioneer claims auction but did not finish contract .call_function("claim", ()) .transaction() .gas(NearGas::from_tgas(30)) .with_signer(auctioneer.account_id().clone(), signer.clone()) ``` --- ## Getting an NFT If you would like to interact with the new contract via the CLI you can mint an NFT from a pre-deployed NFT contract ```bash near call nft.examples.testnet nft_mint '{"token_id": "TYPE_A_UNIQUE_VALUE_HERE", "receiver_id": "", "metadata": { "title": "GO TEAM", "description": "The Team Goes", "media": "https://bafybeidl4hjbpdr6u6xvlrizwxbrfcyqurzvcnn5xoilmcqbxfbdwrmp5m.ipfs.dweb.link/", "copies": 1}}' --useAccount --deposit 0.1 ``` --- ## Conclusion In this part of the tutorial we have added NFTs as a reward which has taught us how to interact with NFT standards, make cross-contract calls and test multiple contracts that interact with each other in workspaces. In the [next part](./3.2-ft.md) we'll learn how to interact with fungible token standards by adapting the auction to receive bids in FTs. This will allow users to launch auctions in different tokens, including stablecoins. --- # Source: https://docs.near.org/tutorials/examples/xcc.md --- id: xcc title: Cross Contract Call description: "Learn how to perform a basic cross-contract call on NEAR to set and retrieve greetings." --- This example performs the simplest cross-contract call possible: it calls our [Hello NEAR](https://github.com/near-examples/hello-near-examples) example to set and retrieve a greeting. It is one of the simplest examples on making a cross-contract call, and the perfect gateway to the world of interoperative contracts. :::info Advanced Cross-Contract Calls Check the tutorial on how to perform cross-contract calls [in batches and in parallel](./advanced-xcc) ::: --- ## Clone the Example You have two options to start the project: 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/cross-contract-calls?quickstart=1) | 🌐 `https://github.com/near-examples/cross-contract-calls` | --- ## Structure of the Example The smart contract is available in two flavors: Rust and JavaScript ```bash ┌── sandbox-ts # sandbox testing │ ├── hello-near │ │ └── hello-near.wasm │ └── main.ava.ts ├── src # contract's code │ └── contract.ts ├── package.json ├── README.md └── tsconfig.json ``` ```bash ┌── tests # sandbox testing │ ├── hello-near │ │ └── hello-near.wasm │ └── tests.rs ├── src # contract's code │ ├── external.rs │ └── lib.rs ├── Cargo.toml # package manager ├── README.md └── rust-toolchain.toml ``` --- ## Smart Contract The contract exposes methods to query the greeting and change it. These methods do nothing but calling `get_greeting` and `set_greeting` in the `hello-near` example.
### Querying for the Greeting The contract performs a cross-contract call to `hello.near-example.testnet` to get the greeting message, and then handles the response in a **callback function**. ``` @call({}) query_greeting(): NearPromise { const promise = NearPromise.new(this.hello_account) .functionCall("get_greeting", NO_ARGS, NO_DEPOSIT, THIRTY_TGAS) .then( NearPromise.new(near.currentAccountId()) .functionCall( "query_greeting_callback", NO_ARGS, NO_DEPOSIT, THIRTY_TGAS, ), ); return promise.asReturn(); } ``` ``` impl Contract { // Public - query external greeting pub fn ll_query_greeting(&self) -> Promise { // Create a promise to call HelloNEAR.get_greeting() let hello_promise = Promise::new(self.hello_account.clone()).function_call( "get_greeting".to_owned(), NO_ARGS, NO_DEPOSIT, TEN_TGAS, ); hello_promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(TEN_TGAS) .ll_query_greeting_callback(), ) } ``` ``` pub fn hl_query_greeting(&self) -> Promise { // Create a promise to call HelloNEAR.get_greeting() let promise = hello_near::ext(self.hello_account.clone()) .with_static_gas(FIVE_TGAS) .get_greeting(); promise.then( // Create a promise to callback query_greeting_callback Self::ext(env::current_account_id()) .with_static_gas(FIVE_TGAS) .hl_query_greeting_callback(), ) } ``` Which requires you to define the external contract interface: ``` #[ext_contract(hello_near)] trait HelloNear { fn get_greeting(&self) -> String; fn set_greeting(&self, greeting: String); } ```
### Callback Function The callback function processes the result of the cross-contract call. In this case, it simply returns the greeting message obtained from the `hello-near` contract. Notice that the callback function is marked as **private**, meaning it can only be called by the contract itself. ``` @call({ privateFunction: true }) query_greeting_callback(): String { let { result, success } = promiseResult(); if (success) { return result.substring(1, result.length - 1); } else { near.log("Promise failed..."); return ""; } } ``` ``` #[private] // Public - but only callable by env::current_account_id() pub fn ll_query_greeting_callback( &self, #[callback_result] call_result: Result, ) -> String { // Check if the promise succeeded by calling the method outlined in external.rs if call_result.is_err() { log!("There was an error contacting Hello NEAR"); return "".to_string(); } // Return the greeting let greeting: String = call_result.unwrap(); greeting } ``` --- ## 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-simple-ts yarn yarn test ``` ```bash cd contract-simple-rs cargo test ``` :::tip The `integration tests` use a sandbox to create NEAR users and simulate interactions with the contract. ::: In this project in particular, the integration tests first deploy the `hello-near` contract. Then, they test that the cross-contract call correctly sets and retrieves the message. You will find the integration tests in `sandbox-ts/` for the JavaScript version and in `tests/` for the Rust version. ``` test.beforeEach(async (t) => { // Create sandbox, accounts, deploy contracts, etc. const worker = t.context.worker = await Worker.init(); // Get root account const root = worker.rootAccount; // Create test accounts const alice = await root.createSubAccount("alice"); const xcc = await root.createSubAccount("xcc"); const helloNear = await root.createSubAccount("hello-near"); // Deploy the hello near contract await helloNear.deploy("./sandbox-ts/hello-near/hello-near.wasm"); // Deploy the xcc contract await xcc.deploy(process.argv[2]); await xcc.call(xcc, "init", { hello_account: helloNear.accountId }); // Save state for test runs, it is unique for each test t.context.accounts = { root, alice, xcc, helloNear }; }); test.afterEach.always(async (t) => { // Stop Sandbox server await t.context.worker.tearDown().catch((error) => { console.log('Failed to stop the Sandbox:', error); }); }); test("returns the default greeting", async (t) => { const { xcc, alice } = t.context.accounts; const greeting = await alice.call(xcc, "query_greeting", {}, { gas: "200000000000000" }); t.is(greeting, 'Hello'); }); test("change the greeting", async (t) => { const { xcc, alice } = t.context.accounts; const howdyChangingResult = await alice.call(xcc, "change_greeting", { new_greeting: "Howdy" }, { gas: "200000000000000" }); t.is(howdyChangingResult, true); const howdyResult = await alice.call(xcc, "query_greeting", {}, { gas: "200000000000000" }); t.is(howdyResult, 'Howdy'); }); ``` ``` #[tokio::test] async fn main() -> Result<(), Box> { let worker = near_workspaces::sandbox().await?; // Build and deploy hello contract let hello_contract_wasm = near_workspaces::compile_project("./tests/hello-near").await?; let hello_contract: Contract = worker.dev_deploy(&hello_contract_wasm).await?; // Deploy contract for testing let contract_wasm = near_workspaces::compile_project("./").await?; let contract = worker.dev_deploy(&contract_wasm).await?; // Create accounts let account = worker.dev_create_account().await?; let alice = account .create_subaccount("alice") .initial_balance(NearToken::from_near(30)) .transact() .await? .into_result()?; // Init contract let _ = contract .call("init") .args_json(json!({ "hello_account": hello_contract.id() })) .transact() .await? .into_result()?; // Begin tests test_hl_default_greeting(&alice, &contract).await?; test_hl_change_greeting(&alice, &contract).await?; test_ll_change_greeting(&alice, &contract).await?; Ok(()) } async fn test_hl_default_greeting( user: &Account, contract: &Contract, ) -> Result<(), Box> { let greeting: String = user .call(contract.id(), "hl_query_greeting") .args_json(json!({})) .max_gas() .transact() .await? .json()?; assert_eq!(greeting, "Hello".to_string()); Ok(()) } async fn test_hl_change_greeting( user: &Account, contract: &Contract, ) -> Result<(), Box> { let result: bool = user .call(contract.id(), "hl_change_greeting") .args_json(json!({ "new_greeting": "Howdy" })) .max_gas() .transact() .await? .json()?; assert!(result); let greeting: String = user .call(contract.id(), "hl_query_greeting") .args_json(json!({})) .max_gas() .transact() .await? .json()?; assert_eq!(greeting, "Howdy".to_string()); ```
### 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-advanced-ts` or `cd contract-advanced-rs`), build and deploy it: ```bash npm run build near deploy ./build/cross_contract.wasm --initFunction new --initArgs '{"hello_account":"hello.near-example.testnet"}' ``` ```bash cargo near deploy build-non-reproducible-wasm with-init-call new json-args '{"hello_account":"hello.near-example.testnet"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' network-config testnet sign-with-keychain send ```
### CLI: Interacting with the Contract To interact with the contract through the console, you can use the following commands: ```bash # Get message from the hello-near contract # Replace with your account ID near call query_greeting --useAccount # Set a new message for the hello-near contract # Replace with your account ID near call change_greeting '{"new_greeting":"XCC Hi"}' --useAccount ``` ```bash # Get message from the hello-near contract # Replace with your account ID near contract call-function as-transaction query_greeting json-args '{}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send # Set a new message for the hello-near contract # Replace with your account ID near contract call-function as-transaction change_greeting json-args '{"new_greeting":"XCC Hi"}' prepaid-gas '100.0 Tgas' attached-deposit '0 NEAR' sign-as network-config testnet sign-with-keychain send ``` --- ## Moving Forward 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. ### Advanced Cross Contract Calls Your contract can perform multiple cross-contract calls in simultaneous, creating promises that execute in parallel, or as a batch transaction. Check the [advanced cross contract calls tutorial](./advanced-xcc) to learn more. :::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` ::: --- # Source: https://docs.near.org/smart-contracts/anatomy/yield-resume.md --- id: yield-resume title: Yield and Resume description: "Wait for an external response and resume execution" --- NEAR smart contracts can **yield** execution, until an **external** service **resumes** them. In practice, the contract yields a **cross-contract call** to itself, until an external service executes a function and the contract decides to resume. This is a powerful feature that allows contracts to wait for external events, such as a response from an oracle, before continuing execution (read our [blog post](/blog/yield-resume)!). :::info The contract can wait for 200 blocks - around 2 minutes - after which the yielded function will execute, receiving a "timeout error" as input ::: --- ## Yielding a Promise Let's look at an example that takes a prompt from a user (e.g. "What is 2+2"), and yields the execution until an external service provides a response. ``` pub fn request(&mut self, prompt: String) { // internal variable to keep track of the requests self.request_id += 1; // this will create a unique ID in the YIELD_REGISTER let yield_promise = env::promise_yield_create( "return_external_response", &json!({ "request_id": self.request_id }) .to_string() .into_bytes(), Gas::from_tgas(5), GasWeight::default(), YIELD_REGISTER, ); // load the ID created by the promise_yield_create let yield_id: CryptoHash = env::read_register(YIELD_REGISTER) .expect("read_register failed") .try_into() .expect("conversion to CryptoHash failed"); // store the request, so we can delete it later let request = Request { yield_id, prompt }; self.requests.insert(self.request_id, request); // return the yield promise env::promise_return(yield_promise); } ``` ```python from near_sdk_py import call, view, near, Context from near_sdk_py.collections import UnorderedMap class YieldResumeContract: def __init__(self): # Store pending requests self.requests = UnorderedMap("r") self.request_id = 0 @call def ask_assistant(self, prompt): """ Creates a yielded promise that will be resumed when an external service responds Args: prompt: The question to ask the assistant """ # Create a new request ID request_id = self.request_id self.request_id += 1 # Create arguments for the callback function callback_args = json.dumps({"request_id": request_id}) # Create a yielded promise to call return_external_response on this contract yield_id = near.promise_create( Context.current_account_id(), "return_external_response", callback_args, 0, # No deposit 30000000000000 # Gas ) # Store the yield ID and prompt for the external service to find self.requests[str(request_id)] = { "yield_id": yield_id, "prompt": prompt } # Return the yield_id and request_id for tracking return { "yield_id": yield_id, "request_id": request_id } ``` #### Creating a Yielded Promise In the example above, we are creating a [`Promise`](./crosscontract.md#promises) to call the contract's function `return_external_response`. Notice that we create the `Promise` using `env::promise_yield_create` in Rust or `near.promise_create` in Python (the Python SDK uses standard promises for yielding), which will create an **identifier** for the yielded promise in the `YIELD_REGISTER`. #### Retrieving the Yielded Promise ID We read the `YIELD_REGISTER` to retrieve the `ID` of our yielded promise. We store the `yield_id` and the user's `prompt` so the external service query them (the contract exposes has a function to list all requests). #### Returning the Promise Finally, we return the `Promise`, which will **not execute immediately**, but will be **yielded** until the external service provides a response.
What is that `self.request_id` in the code? The `self.request_id` is an internal unique identifier that we use to keep track of stored requests. This way, we can delete the request once the external service provides a response (or the waiting times out) Since we only use it to simplify the process of keeping track of the requests, you can remove it if you have a different way of tracking requests (e.g. an indexer)
--- ## Signaling the Resume The `env::promise_yield_resume` function in Rust or `near.promise_yield_resume` in Python allows us to signal which yielded promise should execute, as well as which parameters to pass to the resumed function. ``` pub fn respond(&mut self, yield_id: CryptoHash, response: String) { // resume computation with the response env::promise_yield_resume(&yield_id, &serde_json::to_vec(&response).unwrap()); } ``` ```python @call def respond(self, yield_id, response): """Called by the external service to provide a response to a prompt""" return near.promise_yield_resume(yield_id, json.dumps({"response": response})) ``` In the example above, the `respond` function would be called by an external service, passing which promise should be resume (`yield_id`), and the response to the prompt. :::warning Gatekeeping the Resume Since the function used to signal the resume is public, developers must make sure to guard it properly to avoid unwanted calls. This can be done by simply checking the caller of the function ::: --- ## The Function that Resumes The function being resumed will have access to all parameters passed to it, including those passed during the yield creation, or the external service response. ``` #[private] pub fn return_external_response( &mut self, request_id: u32, #[callback_result] response: Result, ) -> Response { self.requests.remove(&request_id); match response { Ok(answer) => Response::Answer(answer), Err(_) => Response::TimeOutError, } } ``` ```python @call def return_external_response(self, request_id, response=None): """ Function that gets called when the yielded promise resumes Args: request_id: The ID of the request (passed when promise was created) response: The response from the external service (or None if timed out) """ # Remove the request from our tracking request_data = self.requests.get(str(request_id)) if request_data: del self.requests[str(request_id)] # Handle timeout case if response is None: return {"error": "Timeout waiting for external service", "request_id": request_id} # Return the response from the external service return { "request_id": request_id, "response": response, "success": True } ``` In the example above, the `return_external_response` receives parameters: 1. A `request_id` - passed on [creation](#creating-a-yielded-promise) - which is used to remove the request from the state 2. A `response` - passed when [signaling to resume](#signaling-the-resume) - which contains the external response, or `None` if the contract timed out while waiting :::tip There's plenty of time The contract will be able to wait for 200 blocks - around 2 minutes - before timing out ::: :::info Notice that, in this particular example, we choose to return a value both if there is a response or a time out The reason to not raise an error, is because we are changing the state (removing the request in line `#7`), and raising an error would revert this state change ::: --- ## Managing State When using yield and resume, it's important that you carefully manage the contract's state. Because of its asynchronous execution, the contract function in which the contract yields and resumes are independent. If you change the state of the contract in the function where you yield the promise (the `request` function here), then you need to make sure that you revert the state in the function that resumes (the `return_external_response` function here) in the case that the promise times out or the response is invalid. It is best practice to check the validity of the response within the function where the resume is signaled (the `respond` function here) and panic if the response is not valid; the external service can attempt to respond again before the promise times out. You should not panic in `return_external_response` as this is only called when the promise has been resolved (it was resumed or timed out), meaning it can't be resumed again, and the state in `request` has been settled. You should gracefully complete the function and revert the state. :::info Check more docs on [callback security](../security/callbacks.md#async-callbacks) and [reentrancy attacks](../security/reentrancy.md) to avoid common pitfalls when dealing with asynchronous calls ::: --- ## Complete Example Here's a more complete implementation of a yield-resume pattern in Python: ```python from near_sdk_py import view, call, near, Context from near_sdk_py.collections import UnorderedMap class AIAssistantContract: def __init__(self): # Track all pending requests self.requests = UnorderedMap("r") self.request_counter = 0 @call def ask_question(self, question): """ Ask a question to the AI assistant The execution will yield until an external AI service responds """ # Create a unique ID for this request request_id = self.request_counter self.request_counter += 1 # Create callback args - will be passed to our callback function callback_args = json.dumps({ "request_id": request_id }) # Create the promise - this will yield until resumed promise_id = near.promise_create( Context.current_account_id(), # Call this contract "process_ai_response", # Call this method when resumed callback_args, # Pass these arguments 0, # No attached deposit 30000000000000 # Gas for execution (30 TGas) ) # Store the request for the external service to find self.requests[str(request_id)] = { "prompt": question, "promise_id": promise_id, "user": Context.predecessor_account_id(), "timestamp": Context.block_timestamp() } return { "request_id": request_id, "status": "processing" } @view def get_pending_requests(self): """Returns all pending requests for the AI service to process""" return [ { "request_id": int(req_id), "data": self.requests[req_id] } for req_id in self.requests.keys() ] @call def provide_ai_response(self, request_id, response): """ Called by the AI service to provide a response Args: request_id: ID of the request being answered response: The AI's response to the question """ request_id_str = str(request_id) # Verify the request exists if request_id_str not in self.requests: raise Exception(f"No pending request with ID {request_id}") # Get the request data request = self.requests[request_id_str] # Resume the promise with the AI's response result = near.promise_yield_resume( request["promise_id"], json.dumps({"ai_response": response}) ) return {"success": result} @call def process_ai_response(self, request_id, ai_response=None): """ Called when a yielded promise resumes This is either called by provide_ai_response or by a timeout Args: request_id: ID of the request ai_response: The AI's response or None if timed out """ request_id_str = str(request_id) # Cleanup - remove from pending requests if request_id_str in self.requests: request = self.requests[request_id_str] del self.requests[request_id_str] else: request = None # Handle timeout case if ai_response is None: return { "request_id": request_id, "status": "timeout", "message": "The AI service did not respond in time" } # Return the AI's response return { "request_id": request_id, "status": "complete", "question": request["prompt"] if request else "Unknown", "answer": ai_response, "user": request["user"] if request else "Unknown" } ``` This example demonstrates a complete yield-resume pattern for an AI assistant contract where: 1. A user asks a question through `ask_question` 2. The contract creates a yielded promise and stores the question 3. An external AI service periodically checks for new questions using `get_pending_requests` 4. When the AI has an answer, it calls `provide_ai_response` to resume the yielded promise 5. The `process_ai_response` function executes with the AI's answer (or timeout) and returns the result