diff --git a/docs/smart-contracts/anatomy/actions.md b/docs/smart-contracts/anatomy/actions.md index edbbee9ff29..1dc687f8f56 100644 --- a/docs/smart-contracts/anatomy/actions.md +++ b/docs/smart-contracts/anatomy/actions.md @@ -25,23 +25,6 @@ but **cannot** call two methods on different contracts. 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`. - - -```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); - } - } -``` - - - ```rust @@ -61,6 +44,23 @@ You can send $NEAR from your contract to any other account on the network. The G + + +```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 @@ -130,7 +130,47 @@ in a deployed [Hello NEAR](../quickstart.md) contract, and check if everything w 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' @@ -175,46 +215,6 @@ 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") - } - } - } -``` - - - ```python @@ -324,28 +324,6 @@ Sub-accounts are simply useful for organizing your accounts (e.g. `dao.project.n - - -```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) - } - } -``` - - - ```rust @@ -370,6 +348,28 @@ Sub-accounts are simply useful for organizing your accounts (e.g. `dao.project.n + + +```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 @@ -451,31 +451,6 @@ If your contract wants to create a `.mainnet` or `.testnet` account, then it nee the `create_account` method of `near` or `testnet` root contracts. - - -```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); - } - } -``` - - - ```rust @@ -506,6 +481,31 @@ the `create_account` method of `near` or `testnet` root contracts. + + +```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 @@ -690,30 +690,6 @@ There are two options for adding keys to the account:
- - -```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) - } - } -``` - - - ```rust @@ -741,6 +717,30 @@ There are two options for adding keys to the account: + + +```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 @@ -815,36 +815,6 @@ There are two scenarios in which you can use the `delete_account` action: 2. To make your smart contract delete its own account. - - -```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) - } - } -``` - - - ```rust @@ -875,6 +845,36 @@ There are two scenarios in which you can use the `delete_account` action: + + +```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 diff --git a/docs/smart-contracts/anatomy/anatomy.md b/docs/smart-contracts/anatomy/anatomy.md index 52d92bf1ec8..bd65a225648 100644 --- a/docs/smart-contracts/anatomy/anatomy.md +++ b/docs/smart-contracts/anatomy/anatomy.md @@ -12,7 +12,7 @@ import {ExplainCode, Block, File} from '@site/src/components/CodeExplainer/code- 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. - + diff --git a/docs/smart-contracts/anatomy/best-practices.md b/docs/smart-contracts/anatomy/best-practices.md index 35974bf5c68..c180f056902 100644 --- a/docs/smart-contracts/anatomy/best-practices.md +++ b/docs/smart-contracts/anatomy/best-practices.md @@ -15,6 +15,7 @@ This page provides a collection of best practices for writing smart contracts on 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) @@ -25,6 +26,10 @@ Here we lay out some best practices for writing smart contracts on NEAR, such as --- +## 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: diff --git a/docs/smart-contracts/anatomy/collections.md b/docs/smart-contracts/anatomy/collections.md index c24fc50042a..c90e5535101 100644 --- a/docs/smart-contracts/anatomy/collections.md +++ b/docs/smart-contracts/anatomy/collections.md @@ -14,8 +14,6 @@ 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 -Understanding how the contract stores and loads both types of collections is crucial to decide which one to use. - :::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. @@ -24,139 +22,99 @@ If your collection has up to 100 entries, it's acceptable to use the native coll ::: -
+--- - How the State is Handled +## 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 -## Native Collections +Your contract needs to lock a portion of their balance proportional to the amount of data they stored in the blockchain. This means that: -Native collections are those provided by the language: -- JS: `Array`, `Set`, `Map`, `Object` ... -- Rust: `Vector`, `HashMap`, `Set` ... +- 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 ↑**. -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. +Currently, it costs approximately **1 Ⓝ** to store **100kb** of data. + +
- Serialization & Storage Example + Storage Constraints on NEAR -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 +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.
-:::tip When to use them +:::caution -Native collections are useful if you are planning to store smalls amounts of data that need to be accessed all together +Your contract will panic if you try to store data but don't have NEAR to cover its storage cost ::: -:::danger Keep Native Collections Small +:::danger -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 +Be mindful of potential [small deposit attacks](../security/storage.md) ::: --- -## SDK Collections +## Native 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. +Native collections are those provided by the language, such as `Array`, `Map`, `Set` in Javascript, or `Vec`, `HashMap`, `HashSet` in Rust. -These collections are built to have an interface similar to native collections. +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 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`... +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 -:::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 +Native collections are useful if you are planning to store smalls amounts of data that need to be accessed all together ::: -
- -### Exposed Collections - - - - -| SDK Collection | Native Equivalent | Description | -|----------------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `Vector` | `Array` | A growable array type. The values are sharded in memory and can be used for iterable and indexable values that are dynamically sized. | -| `LookupSet` | `Set` | A set, which is similar to `LookupMap` but without storing values, can be used for checking the unique existence of values. This structure is not iterable and can only be used for lookups. | -| `UnorderedSet` | `Set` | An iterable equivalent of `LookupSet` which stores additional metadata for the elements contained in the set. | -| `LookupMap` | `Map` | This structure behaves as a thin wrapper around the key-value storage available to contracts. This structure does not contain any metadata about the elements in the map, so it is not iterable. | -| `UnorderedMap` | `Map` | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. | - - - - - -| SDK collection | `std` equivalent | Description | -|-----------------------------------------------|-----------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `store::Vector` | `Vec` | A growable array type. The values are sharded in memory and can be used for iterable and indexable values that are dynamically sized. | -| store::LookupMap`` | HashMap`` | This structure behaves as a thin wrapper around the key-value storage available to contracts. This structure does not contain any metadata about the elements in the map, so it is not iterable. | -| store::IterableMap`` | HashMap`` | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. | -| store::UnorderedMap`` | HashMap`` | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. | -| `store::LookupSet` | `HashSet` | A set, which is similar to `LookupMap` but without storing values, can be used for checking the unique existence of values. This structure is not iterable and can only be used for lookups. | -| `store::IterableSet` | `HashSet` | An iterable equivalent of `LookupSet` which stores additional metadata for the elements contained in the set. | -| `store::UnorderedSet` | `HashSet` | An iterable equivalent of `LookupSet` which stores additional metadata for the elements contained in the set. | +:::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 - +::: -:::info Note +--- -The `near_sdk::collections` is now deprecated in favor of `near_sdk::store`. To use `near_sdk::collections` you will have to use the [`legacy` feature](https://github.com/near-examples/storage-examples/blob/2a138a6e8915e08ce76718add3e36c04c2ea2fbb/collections-rs/legacy/Cargo.toml#L11). +## 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. -| SDK collection | `std` equivalent | Description | -|----------------------------------------------------|------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `collections::Vector` | `Vec` | A growable array type. The values are sharded in memory and can be used for iterable and indexable values that are dynamically sized. | -| collections::LookupMap`` | HashMap`` | This structure behaves as a thin wrapper around the key-value storage available to contracts. This structure does not contairn any metadata about the elements in the map, so it is not iterable. | -| collections::UnorderedMap`` | HashMap`` | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements in the data structure. | -| collections::TreeMap`` | BTreeMap`` | An ordered equivalent of `UnorderedMap`. The underlying implementation is based on an [AVL tree](https://en.wikipedia.org/wiki/AVL_tree). This structure should be used when a consistent order is needed or accessing the min/max keys is needed. | -| `collections::LookupSet` | `HashSet` | A set, which is similar to `LookupMap` but without storing values, can be used for checking the unique existence of values. This structure is not iterable and can only be used for lookups. | -| `collections::UnorderedSet` | `HashSet` | An iterable equivalent of `LookupSet` which stores additional metadata for the elements contained in the set. | -| `collections::LazyOption` | `Option` | Optional value in storage. This value will only be read from storage when interacted with. This value will be `Some` when the value is saved in storage, and `None` if the value at the prefix does not exist. | - +
- + Serialization & Storage Example -| SDK Collection | Native Equivalent | Description | -| -------------- | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `Vector` | `list` | A growable array type. The values are sharded in memory and can be used for iterable and indexable values that are dynamically sized. | -| `LookupMap` | `dict` | A non-iterable key-value store. This structure does not track keys for iteration, so it is optimized for lookups but cannot provide collection operations like keys or values. | -| `UnorderedMap` | `dict` | Similar to `LookupMap`, except that it stores additional data to be able to iterate through elements and supports dictionary-like operations such as keys(), values(), and items(). | -| `IterableMap` | `dict` | An alias for `UnorderedMap` provided for compatibility with Rust SDK naming conventions. | -| `LookupSet` | `set` | A non-iterable set of unique values. This structure cannot be iterated over and can only be used for membership testing. | -| `UnorderedSet` | `set` | An iterable equivalent of `LookupSet` which stores additional metadata to allow iteration over the values in the set. | -| `IterableSet` | `set` | An alias for `UnorderedSet` provided for compatibility with Rust SDK naming conventions. | -| `TreeMap` | `SortedDict` | An ordered key-value store where keys are maintained in sorted order. Provides operations for range queries, finding nearest keys, and efficient min/max operations. | +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 -### Features | Type | Iterable | Clear All Values | Preserves Insertion Order | Range Selection | |----------------|:--------:|:----------------:|:-------------------------:|:---------------:| | `Vector` | βœ… | βœ… | βœ… | βœ… | @@ -168,9 +126,11 @@ The `near_sdk::collections` is now deprecated in favor of `near_sdk::store`. To | `IterableMap` | βœ… | βœ… | | βœ… | | `TreeMap` | βœ… | βœ… | βœ… | βœ… | -
+
-### Complexity +
+ + SDK Collections' Time Complexities | Type | Access | Insert | Delete | Search | Traverse | Clear | |----------------|:------:|:--------:|:--------:|:--------:|:--------:|:-----:| @@ -185,11 +145,15 @@ The `near_sdk::collections` is now deprecated in favor of `near_sdk::store`. To _\* - 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 Cookbook +SDK collections are useful when you are planning to store large amounts of data that do not need to be accessed all together -Let's see how to use the SDK collections in practice +:::
@@ -198,20 +162,6 @@ Let's see how to use the SDK collections in practice 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 - - - - -:::tip - -Do not forget to use the `schema` to define how your contract's state is structured - -::: - - - - - + + + - :::tip +:::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. +Do not forget to use the `schema` to define how your contract's state is structured - ::: +::: @@ -289,18 +241,14 @@ Be careful of not using the same prefix in two collections, otherwise, their sto 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. - - - - - + + ```python @@ -342,15 +290,15 @@ class VectorExample: 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. + + + - - - ```python from near_sdk_py import view, call, init @@ -391,15 +339,15 @@ class LookupMapExample: 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. + + + - - - ```python from near_sdk_py import view, call, init @@ -445,15 +393,15 @@ Implements a [set](https://en.wikipedia.org/wiki/Set_(abstract_data_type)) which - - - - + + + + ```python @@ -497,15 +445,15 @@ class LookupSetExample: 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. + + + - - - ```python from near_sdk_py import view, call, init @@ -592,17 +540,6 @@ class TreeMapExample: -
- -### LazyOption (Legacy) - -LazyOptions are great to store large values (i.e. a wasm file), since its value will not be read from storage until it is interacted with. - -It acts like an `Option` that can either hold a value or not and also requires a unique prefix (a key in this case) -like other persistent collections. - -Compared to other collections, `LazyOption` only allows you to initialize the value during initialization. - --- ## Nesting Collections @@ -610,13 +547,6 @@ Compared to other collections, `LazyOption` only allows you to initialize the va When nesting SDK collections, be careful to **use different prefixes** for all collections, including the nested ones. - - - - - - + + + + + + ```python @@ -758,65 +695,6 @@ m = IterableSet::new(b"l"); assert!(!m.contains(&1)); ``` -
- -### Nesting Errors - -By extension of the error-prone patterns to avoid mentioned in the [collections section](./collections.md#error-prone-patterns), it is important to keep in mind how these bugs can easily be introduced into a contract when using nested collections. - -Some issues for more context: -- https://github.com/near/near-sdk-rs/issues/560 -- https://github.com/near/near-sdk-rs/issues/703 - -The following cases are the most commonly encountered bugs that cannot be restricted at the type level (only relevant for `near_sdk::collections`, not `near_sdk::store`): - -```rust -use near_sdk::borsh::{self, BorshSerialize}; -use near_sdk::collections::{LookupMap, UnorderedSet}; -use near_sdk::BorshStorageKey; - -#[derive(BorshStorageKey, BorshSerialize)] -pub enum StorageKey { - Root, - Nested(u8), -} - -// Bug 1: Nested collection is removed without clearing its own state. -let mut root: LookupMap> = LookupMap::new(StorageKey::Root); -let mut nested = UnorderedSet::new(StorageKey::Nested(1)); -nested.insert(&"test".to_string()); -root.insert(&1, &nested); - -// Remove inserted collection without clearing its sub-state. -let mut _removed = root.remove(&1).unwrap(); - -// This line would fix the bug: -// _removed.clear(); - -// This collection will now be in an inconsistent state if an empty UnorderedSet is put -// in the same entry of `root`. -root.insert(&1, &UnorderedSet::new(StorageKey::Nested(1))); -let n = root.get(&1).unwrap(); -assert!(n.is_empty()); -assert!(n.contains(&"test".to_string())); - -// Bug 2: Nested collection is modified without updating the collection itself in the outer collection. -// -// This is fixed at the type level in `near_sdk::store` because the values are modified -// in-place and guarded by regular Rust borrow-checker rules. -root.insert(&2, &UnorderedSet::new(StorageKey::Nested(2))); - -let mut nested = root.get(&2).unwrap(); -nested.insert(&"some value".to_string()); - -// This line would fix the bug: -// root.insert(&2, &nested); - -let n = root.get(&2).unwrap(); -assert!(n.is_empty()); -assert!(n.contains(&"some value".to_string())); -``` -
@@ -829,20 +707,6 @@ 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 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); - } - ``` - - 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. @@ -870,6 +734,20 @@ In order to expose them all through view calls, we can use pagination. + + 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. @@ -896,44 +774,3 @@ In order to expose them all through view calls, we can use pagination. ``` - ---- - -## 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 and the **storage increases ↑**, then your contract's **balance decreases ↓**. -- If data is deleted and the **storage decreases ↓**, then your contract's **balance increases ↑**. - -Currently, it costs approximately **1 Ⓝ** to store **100kb** of data. - -:::info - -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 - -::: - -:::caution - -Your contract will panic if you try to store data but don't have NEAR to cover its storage cost - -::: - -:::warning - -Be mindful of potential [small deposit attacks](../security/storage.md) - -::: - ---- - -## 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. diff --git a/docs/smart-contracts/anatomy/crosscontract.md b/docs/smart-contracts/anatomy/crosscontract.md index 73d97d363cb..1dcaa080525 100644 --- a/docs/smart-contracts/anatomy/crosscontract.md +++ b/docs/smart-contracts/anatomy/crosscontract.md @@ -4,25 +4,22 @@ title: Cross-Contract Calls description: "Contract can interact with other contracts on the network" --- -import {CodeTabs, Language, Github} from '@site/src/components/UI/Codetabs' import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; +import {Github} from '@site/src/components/UI/Codetabs' +import Card from '@site/src/components/UI/Card'; -Cross-contract calls are a powerful feature of NEAR smart contracts, allowing one contract to interact with another. This enables complex interactions and functionalities across different contracts, enhancing the ecosystem's capabilities. +NEAR contracts can interact with other deployed contracts, querying information and executing functions on them through cross-contract calls. -Your contract can interact with other deployed contracts, **querying** information and **executing functions** on them. - -Since NEAR is a sharded blockchain, its cross-contract calls behave differently than calls do in other chains. In NEAR. cross-contract calls are asynchronous and independent. - -:::info Cross-Contract Calls are **Independent** - -You will need two independent functions: one to make the call, and another to receive the result +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. ::: -:::info Cross-Contract Calls are **Asynchronous** +:::tip Independent -There is a delay between the call and the callback execution, usually of **1 or 2 blocks**. During this time, the contract is still active and can receive other calls. +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. ::: @@ -32,31 +29,30 @@ There is a delay between the call and the callback execution, usually of **1 or 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. - - - + + + + + + + + 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) + + + + + + + - - - - - - - - - - - + ```python from near_sdk_py import call, view, Contract, callback, PromiseResult, CrossContract, init @@ -104,9 +100,9 @@ class CrossContractExample(Contract): "message": f"Successfully got greeting: {result.data}" } ``` - +
- + ```go package main @@ -159,40 +155,39 @@ func (c *Contract) ExampleQueryingInformationResponse(result promise.PromiseResu } } ``` - - - +
+
--- ## 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. - - - + + + + + + + + + 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) + + + + + + + - - - - - - - - - - - + ```python from near_sdk_py import call, Contract, callback, PromiseResult, CrossContract @@ -234,12 +229,10 @@ class CrossContractExample(Contract): "result": result.data } ``` - +
- + ```go - - package main import ( @@ -283,24 +276,23 @@ func (c *Contract) ExampleChangeGreetingCallback(result promise.PromiseResult) { } } ``` - - - +
+
--- ## 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`) +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 (**encoded**) arguments to pass to the function -- The amount of GAS to use (deducted from the **attached Gas**) -- The amount of NEAR to attach (deducted from **your contract's balance**) +- 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 @@ -308,15 +300,62 @@ The callback can be made to **any** contract. Meaning that the result could pote ::: +--- -
- -### Creating a Cross Contract Call +## 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 @@ -325,152 +364,79 @@ To create a cross-contract call with a callback, create two promises and use the // this function is the callback NearPromise.new(near.currentAccountId()).functionCall("callback_name", JSON.stringify(arguments), DEPOSIT, GAS) ); - ``` - - - - - There is a helper macro that allows you to make cross-contract calls with the syntax `#[ext_contract(...)]`. It takes a Rust Trait and converts it to a module with static methods. Each of these static methods takes positional arguments defined by the Trait, then the `receiver_id`, the attached deposit and the amount of gas and returns a new `Promise`. *That's the high-level way to make cross-contract calls.* - - ```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) - ); - - ``` - -
- - There is another way to achieve the same result. You can create a new `Promise` without using a helper macro. *It's the low-level way to make cross-contract calls.* - - ```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(), - ); - - ``` - -
- Gas - -You can attach an unused GAS weight by specifying the `.with_unused_gas_weight()` method but it is defaulted to 1. The unused GAS will be split amongst all the functions in the current execution depending on their weights. If there is only 1 function, any weight above 1 will result in all the unused GAS being attached to that function. If you specify a weight of 0, however, the unused GAS will **not** be attached to that function. If you have two functions, one with a weight of 3, and one with a weight of 1, the first function will get `3/4` of the unused GAS and the other function will get `1/4` of the unused GAS. - -
- + ``` +
+ - - - ```python - from near_sdk_py import Contract, Context, ONE_TGAS - - # High-level Contract API (recommended) - CrossContract("external_address").call( - "function_name", # Method to call - arg1="value1", # Keyword arguments for the method - arg2="value2" - ).then( - "callback_name", # Method name in this contract to use as callback - context_data="saved_for_callback" # Additional context data for the callback - ).value() - - # Lower-level Promise API - from near_sdk_py import Promise - - Promise.create_batch("external_address").function_call( - "function_name", - {"arg1": "value1", "arg2": "value2"}, # Arguments as a dictionary - amount=0, # Deposit in yoctoNEAR - gas=5 * ONE_TGAS # Gas allowance - ).then( - Context.current_account_id() # The contract to call for the callback - ).function_call( - "callback_name", # Method name for callback - {"context_data": "saved_for_callback"} # Arguments for the callback - ).value() - ``` - + ```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") + } + } + ``` - -```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{} + Concatenating Promises -type PromiseCallbackInputData struct { - Data string `json:"data"` -} +βœ… 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` -// @contract:payable min_deposit=0.00001NEAR -func (c *Contract) ExampleCrossContractCall() { - externalAccount := "hello-nearverse.testnet" - gas := uint64(5 * types.ONE_TERA_GAS) +βœ… 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** - 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() -} +β›” You cannot **return** a joint promise without a callback: `return P1.and(P2)` is invalid, you need to add a `.then()` -// @contract:view -// @contract:promise_callback -func (c *Contract) ExampleCrossContractCallback(input PromiseCallbackInputData, result promise.PromiseResult) { - env.LogString("Executing callback") +β›” You cannot join promises within a `then`: `P1.then(P2.join([P3]))` is invalid - env.LogString("Input CrossContractCallback : " + input.Data) +β›” You cannot use a `then` within a `then`: `P1.then(P2.then(P3))` is invalid - if result.Success { - env.LogString("Cross-contract call executed successfully") - } else { - env.LogString("Cross-contract call failed") - } -} -``` - - +
:::info @@ -492,23 +458,20 @@ If your function finishes correctly, then eventually your callback function will 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. - - - - - - - - - + + + - + + + - + ```python from near_sdk_py import callback, PromiseResult, Contract @@ -542,11 +505,10 @@ class CrossContractExample(Contract): "context": additional_context } ``` - +
- + ```go - type PromiseCallbackInputData struct { Data string `json:"data"` } @@ -565,9 +527,8 @@ func (c *Contract) ExampleCrossContractCallback(input PromiseCallbackInputData, } } ``` - - - +
+
:::info Callback with always execute @@ -604,40 +565,26 @@ operation if necessary. --- -## Concatenating Functions and Promises - -βœ… Promises can be concatenate using the `.join` operator: `P1.join([P2, P3], "callback")`: `P1`, `P2`, and `P3` execute in parallel, after they finish, the callback will execute and have access to all their results - -β›” You cannot **return** a joint promise without a callback: `return P1.join([P2])` is invalid since it misses the callback parameter - -βœ… You can concatenate `then` promises: `P1.then("callback1").then("callback2")`: `P1` executes, then callback1 executes with the result of `P1`, then callback2 executes with the result of callback1 - -β›” You cannot use a `join` within a `then`: `P1.then(P2.join([P3]))` is invalid - -β›” You cannot use a `then` within a `then`: `P1.then(P2.then("callback"))` is invalid - -
- -### Multiple Functions, Same Contract +## 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**. - - - - - + + + + + @@ -755,26 +702,22 @@ Callbacks only have access to the result of the **last function** in a batch cal --- -### Multiple Functions: Different Contracts +## 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**. - - - - - - - - - + + + + ```python @@ -895,7 +838,6 @@ Callbacks have access to the result of **all functions** in a parallel call ::: - --- ## Security Concerns diff --git a/docs/smart-contracts/anatomy/environment.md b/docs/smart-contracts/anatomy/environment.md index e55cf87d907..077fe54a740 100644 --- a/docs/smart-contracts/anatomy/environment.md +++ b/docs/smart-contracts/anatomy/environment.md @@ -22,26 +22,6 @@ Every method execution has an environment associated with information such as: ## Environment Variables - - -| 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 | @@ -63,6 +43,26 @@ Every method execution has an environment associated with information such as: + + +| 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 | @@ -234,21 +234,6 @@ def check_gas(required_gas=20_000_000_000_000): # 20 TGas Besides environmental variables, the SDK also exposes some functions to perform basic cryptographic operations - - -| 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 | @@ -268,6 +253,21 @@ Besides environmental variables, the SDK also exposes some functions to perform + + +| 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 | diff --git a/docs/smart-contracts/anatomy/functions.md b/docs/smart-contracts/anatomy/functions.md index 8fcdd13ea99..049005dd578 100644 --- a/docs/smart-contracts/anatomy/functions.md +++ b/docs/smart-contracts/anatomy/functions.md @@ -13,7 +13,7 @@ import {ExplainCode, Block, File} from '@site/src/components/CodeExplainer/code- Smart contracts expose functions so users can interact with them. There are different types of functions including `read-only`, `private` and `payable`. - + diff --git a/docs/smart-contracts/anatomy/storage.md b/docs/smart-contracts/anatomy/storage.md index 4249c5bf028..79ea00f99dd 100644 --- a/docs/smart-contracts/anatomy/storage.md +++ b/docs/smart-contracts/anatomy/storage.md @@ -17,7 +17,7 @@ It is important to know that the account's **code** and account's **storage** ar
- + diff --git a/docs/smart-contracts/anatomy/types.md b/docs/smart-contracts/anatomy/types.md index 37588a53853..9d388c6ce91 100644 --- a/docs/smart-contracts/anatomy/types.md +++ b/docs/smart-contracts/anatomy/types.md @@ -14,7 +14,7 @@ import {ExplainCode, Block, File} from '@site/src/components/CodeExplainer/code- 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. - + diff --git a/docs/smart-contracts/release/deploy.md b/docs/smart-contracts/release/deploy.md index 6ef5a2b4667..87444082b15 100644 --- a/docs/smart-contracts/release/deploy.md +++ b/docs/smart-contracts/release/deploy.md @@ -29,18 +29,18 @@ Thanks to the `NEAR CLI` deploying a contract is as simple as: ### Compile the Contract - + ```bash - yarn build + cargo near build ``` - + ```bash - cargo near build + yarn build ``` diff --git a/docs/smart-contracts/release/upgrade.md b/docs/smart-contracts/release/upgrade.md index c9b582572e3..e31886c0654 100644 --- a/docs/smart-contracts/release/upgrade.md +++ b/docs/smart-contracts/release/upgrade.md @@ -138,14 +138,6 @@ for such messages to be "premium". You keep track of the messages and payments using the following state: - - - - - - + + + + + + #### Update Contract @@ -162,14 +162,6 @@ At some point you realize that you could keep track of the `payments` inside of the `PostedMessage` itself, so you change the contract to: - - - - - - + + + + + + #### Incompatible States @@ -197,14 +197,6 @@ state, removes the `payments` vector and adds the information to the `PostedMessages`: - - - - - - + + + + + + Notice that `migrate` is actually an diff --git a/docs/smart-contracts/tutorials/basic-contracts.md b/docs/smart-contracts/tutorials/basic-contracts.md index 547e161b848..f299c76efda 100644 --- a/docs/smart-contracts/tutorials/basic-contracts.md +++ b/docs/smart-contracts/tutorials/basic-contracts.md @@ -185,20 +185,20 @@ The contracts are implemented following the latest versions of each SDK, and inc 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-rs -cargo test ``` @@ -235,20 +235,20 @@ Here we are using the `--useFaucet` flag to create a new account and pre-fund it 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-rs -cargo near deploy build-non-reproducible-wasm ``` diff --git a/website/static/css/custom.scss b/website/static/css/custom.scss index b006d6aa7d0..01005f25d63 100644 --- a/website/static/css/custom.scss +++ b/website/static/css/custom.scss @@ -603,6 +603,11 @@ video+p>em,img+p>em,img+em, .monaco+em { // CODE & MONACO EDITOR STYLES // ============================================================================ +.theme-code-block { + overflow: scroll; + max-height: 100vh; +} + .monaco { border-radius: 10px; background-color: #f6f8fa;