diff --git a/.github/workflows/add-to-devtools.yml b/.github/workflows/add-to-devtools.yml
new file mode 100644
index 000000000..35bd989f8
--- /dev/null
+++ b/.github/workflows/add-to-devtools.yml
@@ -0,0 +1,22 @@
+name: 'Add to DevTools Project'
+
+on:
+ issues:
+ types:
+ - opened
+ - reopened
+ pull_request:
+ types:
+ - opened
+ - reopened
+
+jobs:
+ add-to-project:
+ name: Add issue/PR to project
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/add-to-project@v1.0.0
+ with:
+ # add to DevTools Project #156
+ project-url: https://github.com/orgs/near/projects/156
+ github-token: ${{ secrets.GH_TOKEN }}
diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml
index ecfd50dc7..489343d1d 100644
--- a/.github/workflows/coverage.yml
+++ b/.github/workflows/coverage.yml
@@ -16,7 +16,7 @@ jobs:
platform: [macos-latest]
toolchain: [stable]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install Homebrew
run: |
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
diff --git a/.github/workflows/release-plz.yml b/.github/workflows/release-plz.yml
index 22141ea19..21c7e358e 100644
--- a/.github/workflows/release-plz.yml
+++ b/.github/workflows/release-plz.yml
@@ -15,7 +15,7 @@ jobs:
if: github.ref == 'refs/heads/master'
steps:
- name: Checkout repository
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ secrets.CUSTOM_GITHUB_TOKEN }}
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index a13726c93..06bda06c8 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -15,16 +15,16 @@ jobs:
matrix:
platform:
- os: ubuntu-latest
- rs: 1.80.0
+ rs: 1.82.0
- os: ubuntu-latest
rs: stable
- os: macos-latest
- rs: 1.80.0
+ rs: 1.82.0
- os: macos-latest
rs: stable
features: ['', '--features unstable,legacy,__abi-generate']
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: "${{ matrix.platform.rs }} with rustfmt, and wasm32"
uses: actions-rs/toolchain@v1
with:
@@ -45,7 +45,7 @@ jobs:
name: Clippy and fmt
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
@@ -61,7 +61,7 @@ jobs:
name: Compilation tests
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
@@ -75,7 +75,7 @@ jobs:
name: Windows
runs-on: windows-latest
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: "Setup Windows toolchain"
uses: actions-rs/toolchain@v1
with:
@@ -93,7 +93,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Sources
- uses: actions/checkout@v3
+ uses: actions/checkout@v4
- name: Install Toolchain
uses: actions-rs/toolchain@v1
with:
@@ -105,3 +105,25 @@ jobs:
run: cargo install cargo-audit
- name: Run Audit
run: cargo audit
+ # there're sometimes warnings, which signal, that the generated doc
+ # won't look as expected, when rendered, and sometimes errors, which will prevent doc from being
+ # generated at release time altogether.
+ cargo-doc:
+ runs-on: ubuntu-20.04
+
+ steps:
+ - uses: actions/checkout@v4
+ - name: Install Toolchain
+ uses: actions-rs/toolchain@v1
+ with:
+ profile: minimal
+ toolchain: stable
+ default: true
+ - name: run cargo doc
+ env:
+ RUSTDOCFLAGS: -D warnings
+ run: |
+ cargo doc -p near-sdk --features unstable,legacy,unit-testing,__macro-docs
+ cargo doc -p near-sdk-macros
+ cargo doc -p near-contract-standards --no-deps
+ cargo doc -p near-sys
diff --git a/.github/workflows/test_examples.yml b/.github/workflows/test_examples.yml
index d8790f7c8..278b8a904 100644
--- a/.github/workflows/test_examples.yml
+++ b/.github/workflows/test_examples.yml
@@ -25,7 +25,7 @@ jobs:
factory-contract
]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: "${{ matrix.toolchain }} with rustfmt, and wasm32"
uses: actions-rs/toolchain@v1
with:
diff --git a/.github/workflows/test_examples_small.yml b/.github/workflows/test_examples_small.yml
index fde99636e..9c3f41e9a 100644
--- a/.github/workflows/test_examples_small.yml
+++ b/.github/workflows/test_examples_small.yml
@@ -22,7 +22,7 @@ jobs:
test-contract,
]
steps:
- - uses: actions/checkout@v3
+ - uses: actions/checkout@v4
- name: "${{ matrix.toolchain }} with rustfmt, clippy, and wasm32"
uses: actions-rs/toolchain@v1
with:
diff --git a/.github/workflows/typo.yml b/.github/workflows/typo.yml
index 021c55fd4..6bbce31c0 100644
--- a/.github/workflows/typo.yml
+++ b/.github/workflows/typo.yml
@@ -7,7 +7,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Actions Repository
- uses: actions/checkout@v2
+ uses: actions/checkout@v4
- name: Check spelling of the entire repository
uses: crate-ci/typos@v1.11.1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 6ecacd790..3326b8283 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,17 @@
## [Unreleased]
+## [5.7.0](https://github.com/near/near-sdk-rs/compare/near-sdk-v5.6.0...near-sdk-v5.7.0) - 2024-12-13
+
+### Other
+
+- updates near-* dependencies to 0.28 release (#1272)
+- tests for Lazy and moving out of unstable (#1268)
+- add a `cargo doc` job (#1269)
+- allow clippy::needless_lifetimes (1.83 more suggestions) (#1267)
+- examples for Near-related host functions (#1259)
+- updates near-workspaces to 0.15 version (#1260)
+
## [5.6.0](https://github.com/near/near-sdk-rs/compare/near-sdk-v5.5.0...near-sdk-v5.6.0) - 2024-11-14
### Other
diff --git a/CODEOWNERS b/CODEOWNERS
index 3144a1fea..ab35564a2 100644
--- a/CODEOWNERS
+++ b/CODEOWNERS
@@ -1 +1 @@
-* @frol @dj8yfo @ruseinov @akorchyn
+* @frol @dj8yfo @ruseinov @akorchyn @PolyProgrammist
diff --git a/Cargo.toml b/Cargo.toml
index c2d3824b4..1d2f5397f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,7 @@ members = ["near-sdk", "near-sdk-macros", "near-contract-standards", "near-sys"]
exclude = ["examples/"]
[workspace.package]
-version = "5.6.0"
+version = "5.7.0"
# Special triple # comment for ci.
[patch.crates-io]
diff --git a/README.md b/README.md
index ff2e36d0c..be5022f74 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -321,7 +321,7 @@ State breaking changes (low-level serialization format of any data type) will be
### MSRV
-The minimum supported Rust version is currently `1.80`. There are no guarantees that this will be upheld if a security patch release needs to come in that requires a Rust toolchain increase.
+The minimum supported Rust version is currently `1.82`. There are no guarantees that this will be upheld if a security patch release needs to come in that requires a Rust toolchain increase.
## Contributing
diff --git a/examples/fungible-token/Cargo.toml b/examples/fungible-token/Cargo.toml
index 535e8f49b..b62008b0f 100644
--- a/examples/fungible-token/Cargo.toml
+++ b/examples/fungible-token/Cargo.toml
@@ -9,7 +9,6 @@ anyhow = "1.0"
near-sdk = { path = "../../near-sdk", features = ["unit-testing"] }
tokio = { version = "1.14", features = ["full"] }
near-workspaces = { version = "0.15", features = ["unstable"] }
-cargo-near-build = "0.3.1"
rstest = "0.23.0"
[profile.release]
diff --git a/examples/fungible-token/tests/workspaces.rs b/examples/fungible-token/tests/workspaces.rs
index e8f19d673..c3731540f 100644
--- a/examples/fungible-token/tests/workspaces.rs
+++ b/examples/fungible-token/tests/workspaces.rs
@@ -5,6 +5,7 @@ use near_workspaces::operations::Function;
use near_workspaces::result::ValueOrReceiptId;
use near_workspaces::{types::NearToken, Account, AccountId, Contract};
use rstest::{fixture, rstest};
+use near_workspaces::cargo_near_build;
const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1);
diff --git a/examples/lockable-fungible-token/Cargo.toml b/examples/lockable-fungible-token/Cargo.toml
index 4163aa549..73f688670 100644
--- a/examples/lockable-fungible-token/Cargo.toml
+++ b/examples/lockable-fungible-token/Cargo.toml
@@ -15,7 +15,6 @@ anyhow = "1.0"
tokio = { version = "1.14", features = ["full"] }
near-sdk = { path = "../../near-sdk", features = ["unit-testing"] }
near-workspaces = { version = "0.15", features = ["unstable"] }
-cargo-near-build = "0.3.1"
rstest = "0.23.0"
[profile.release]
diff --git a/examples/lockable-fungible-token/tests/workspaces.rs b/examples/lockable-fungible-token/tests/workspaces.rs
index a48e95aaf..c3ef06f6d 100644
--- a/examples/lockable-fungible-token/tests/workspaces.rs
+++ b/examples/lockable-fungible-token/tests/workspaces.rs
@@ -3,6 +3,7 @@ use std::str::FromStr;
use near_sdk::json_types::U128;
use near_workspaces::{types::NearToken, Account, Contract};
use rstest::{fixture, rstest};
+use near_workspaces::cargo_near_build;
#[fixture]
fn initial_balance() -> U128 {
diff --git a/examples/non-fungible-token/Cargo.toml b/examples/non-fungible-token/Cargo.toml
index 27754d781..151fd9501 100644
--- a/examples/non-fungible-token/Cargo.toml
+++ b/examples/non-fungible-token/Cargo.toml
@@ -11,7 +11,6 @@ near-sdk = { path = "../../near-sdk", features = ["unit-testing"] }
tokio = { version = "1.14", features = ["full"] }
near-workspaces = { version = "0.15", features = ["unstable"] }
rstest = "0.23.0"
-cargo-near-build = "0.3.1"
[profile.release]
codegen-units = 1
diff --git a/examples/non-fungible-token/tests/workspaces/utils.rs b/examples/non-fungible-token/tests/workspaces/utils.rs
index accb4861e..5b926c735 100644
--- a/examples/non-fungible-token/tests/workspaces/utils.rs
+++ b/examples/non-fungible-token/tests/workspaces/utils.rs
@@ -6,7 +6,7 @@ use near_contract_standards::non_fungible_token::TokenId;
use near_workspaces::types::NearToken;
use near_workspaces::{Account, Contract};
use rstest::fixture;
-
+use near_workspaces::cargo_near_build;
pub const TOKEN_ID: &str = "0";
pub async fn helper_mint(
diff --git a/near-contract-standards/CHANGELOG.md b/near-contract-standards/CHANGELOG.md
index f512e5ceb..b202003e4 100644
--- a/near-contract-standards/CHANGELOG.md
+++ b/near-contract-standards/CHANGELOG.md
@@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+## [5.7.0](https://github.com/near/near-sdk-rs/compare/near-contract-standards-v5.6.0...near-contract-standards-v5.7.0) - 2024-12-13
+
+### Other
+
+- add a `cargo doc` job (#1269)
+- allow clippy::needless_lifetimes (1.83 more suggestions) (#1267)
+
## [5.5.0](https://github.com/near/near-sdk-rs/compare/near-contract-standards-v5.4.0...near-contract-standards-v5.5.0) - 2024-09-11
### Other
diff --git a/near-contract-standards/Cargo.toml b/near-contract-standards/Cargo.toml
index 03ff3cdab..e4ab1dfe9 100644
--- a/near-contract-standards/Cargo.toml
+++ b/near-contract-standards/Cargo.toml
@@ -13,7 +13,7 @@ NEAR smart contracts standard library.
"""
[dependencies]
-near-sdk = { path = "../near-sdk", version = "~5.6.0", default-features = false, features = [
+near-sdk = { path = "../near-sdk", version = "~5.7.0", default-features = false, features = [
"legacy",
] }
diff --git a/near-contract-standards/src/fungible_token/mod.rs b/near-contract-standards/src/fungible_token/mod.rs
index eb0698fca..1210748ab 100644
--- a/near-contract-standards/src/fungible_token/mod.rs
+++ b/near-contract-standards/src/fungible_token/mod.rs
@@ -1,3 +1,5 @@
+//! Fungible tokens as described in [by the spec](https://nomicon.io/Standards/Tokens/FungibleToken).
+//!
//! This module represents a Fungible Token standard.
//!
//! # Examples
diff --git a/near-contract-standards/src/lib.rs b/near-contract-standards/src/lib.rs
index d6e548041..edd387d12 100644
--- a/near-contract-standards/src/lib.rs
+++ b/near-contract-standards/src/lib.rs
@@ -1,12 +1,15 @@
// We want to enable all clippy lints, but some of them generate false positives.
#![allow(clippy::missing_const_for_fn, clippy::redundant_pub_crate)]
+#![allow(clippy::needless_lifetimes)]
-/// Fungible tokens as described in [by the spec](https://nomicon.io/Standards/FungibleToken/README.html).
pub mod fungible_token;
-/// Non-fungible tokens as described in [by the spec](https://nomicon.io/Standards/NonFungibleToken/README.html).
+
+/// Non-fungible tokens as described in [by the spec](https://nomicon.io/Standards/Tokens/NonFungibleToken).
pub mod non_fungible_token;
+
/// Storage management deals with handling [state storage](https://docs.near.org/docs/concepts/storage-staking) on NEAR. This follows the [storage management standard](https://nomicon.io/Standards/StorageManagement.html).
pub mod storage_management;
+
/// This upgrade standard is a use case where a staging area exists for a WASM
/// blob, allowing it to be stored for a period of time before deployed.
#[deprecated(
diff --git a/near-sdk/Cargo.toml b/near-sdk/Cargo.toml
index 6e7750f55..853cc17a0 100644
--- a/near-sdk/Cargo.toml
+++ b/near-sdk/Cargo.toml
@@ -21,7 +21,7 @@ required-features = ["abi", "unstable"]
# Provide near_bidgen macros.
serde = { version = "1", features = ["derive"] }
serde_json = "1"
-near-sdk-macros = { path = "../near-sdk-macros", version = "~5.6.0" }
+near-sdk-macros = { path = "../near-sdk-macros", version = "~5.7.0" }
near-sys = { path = "../near-sys", version = "0.2.2" }
base64 = "0.22"
borsh = { version = "1.0.0", features = ["derive"] }
@@ -42,11 +42,11 @@ schemars = { version = "0.8.8", optional = true }
near-abi = { version = "0.4.0", features = [
"__chunked-entries",
], optional = true }
-near-vm-runner = { version = "0.27", optional = true }
-near-primitives-core = { version = "0.27", optional = true }
-near-primitives = { version = "0.27", optional = true }
-near-crypto = { version = "0.27", default-features = false, optional = true }
-near-parameters = { version = "0.27", optional = true }
+near-vm-runner = { version = "0.28", optional = true }
+near-primitives-core = { version = "0.28", optional = true }
+near-primitives = { version = "0.28", optional = true }
+near-crypto = { version = "0.28", default-features = false, optional = true }
+near-parameters = { version = "0.28", optional = true }
[dev-dependencies]
near-sdk = { path = ".", features = ["legacy", "unit-testing"] }
diff --git a/near-sdk/src/environment/env.rs b/near-sdk/src/environment/env.rs
index d5474837f..d7ee2abad 100644
--- a/near-sdk/src/environment/env.rs
+++ b/near-sdk/src/environment/env.rs
@@ -152,23 +152,59 @@ pub fn register_len(register_id: u64) -> Option {
// # 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(), AccountId::from_str("alice.near").unwrap());
+/// ```
pub fn current_account_id() -> AccountId {
assert_valid_account_id(method_into_register!(current_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(), AccountId::from_str("bob.near").unwrap());
+/// ```
pub fn signer_account_id() -> AccountId {
assert_valid_account_id(method_into_register!(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 {
PublicKey::try_from(method_into_register!(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(), AccountId::from_str("bob.near").unwrap());
+/// ```
pub fn predecessor_account_id() -> AccountId {
assert_valid_account_id(method_into_register!(predecessor_account_id))
}
@@ -182,37 +218,86 @@ fn assert_valid_account_id(bytes: Vec) -> AccountId {
}
/// 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()));
+/// ```
pub fn input() -> Option> {
try_method_into_register!(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 {
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 {
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 {
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() }
}
@@ -222,6 +307,14 @@ pub fn storage_usage() -> StorageUsage {
// #################
/// 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 data = [0u8; size_of::()];
unsafe { sys::account_balance(data.as_ptr() as u64) };
@@ -229,6 +322,14 @@ pub fn account_balance() -> NearToken {
}
/// 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 data = [0u8; size_of::()];
unsafe { sys::account_locked_balance(data.as_ptr() as u64) };
@@ -237,6 +338,14 @@ pub fn account_locked_balance() -> NearToken {
/// 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 {
let data = [0u8; size_of::()];
unsafe { sys::attached_deposit(data.as_ptr() as u64) };
@@ -244,11 +353,27 @@ pub fn attached_deposit() -> NearToken {
}
/// 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 {
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() })
}
@@ -260,6 +385,13 @@ pub fn used_gas() -> Gas {
/// 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()
}
@@ -326,7 +458,6 @@ pub fn random_seed_array() -> [u8; 32] {
/// Hashes the random sequence of bytes using sha256.
///
/// # Examples
-///
/// ```
/// use near_sdk::env::sha256;
/// use hex;
@@ -341,11 +472,35 @@ pub fn sha256(value: &[u8]) -> 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: &[u8]) -> Vec {
keccak256_array(value).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: &[u8]) -> Vec {
keccak512_array(value).to_vec()
}
@@ -353,7 +508,6 @@ pub fn keccak512(value: &[u8]) -> 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;
@@ -376,6 +530,19 @@ pub fn sha256_array(value: &[u8]) -> [u8; 32] {
}
/// 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: &[u8]) -> [u8; 32] {
//* 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
@@ -387,6 +554,19 @@ pub fn keccak256_array(value: &[u8]) -> [u8; 32] {
}
/// 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: &[u8]) -> [u8; 64] {
//* 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
@@ -398,6 +578,19 @@ pub fn keccak512_array(value: &[u8]) -> [u8; 64] {
}
/// 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: &[u8]) -> [u8; 20] {
//* 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
@@ -441,6 +634,46 @@ pub fn ecrecover(
}
/// 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: &[u8], public_key: &[u8; 32]) -> bool {
unsafe {
sys::ed25519_verify(
@@ -501,6 +734,24 @@ pub fn alt_bn128_pairing_check(value: &[u8]) -> bool {
/// 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(
+/// 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)
+/// );
+/// ```
+///
/// More info about promises in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/crosscontract#promises)
pub fn promise_create(
account_id: AccountId,
@@ -525,6 +776,35 @@ pub fn promise_create(
}
/// Attaches the callback 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(
+/// 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)
+/// );
+///
+/// let chained_promise = promise_then(
+/// promise,
+/// AccountId::from_str("greetings.near").unwrap(),
+/// "set_greeting",
+/// serde_json::json!({
+/// "text": "Hello World"
+/// }).to_string().into_bytes().as_slice(),
+/// NearToken::from_yoctonear(4000000000000),
+/// Gas::from_tgas(30)
+/// );
+/// ```
pub fn promise_then(
promise_idx: PromiseIndex,
account_id: AccountId,
@@ -550,6 +830,36 @@ pub fn promise_then(
}
/// 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(
+/// 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)
+/// );
+///
+/// let promise2 = promise_create(
+/// AccountId::from_str("greetings.near").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]);
+/// ```
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() {
@@ -559,6 +869,16 @@ pub fn promise_and(promise_indices: &[PromiseIndex]) -> PromiseIndex {
unsafe { PromiseIndex(sys::promise_and(data.as_ptr() as _, promise_indices.len() as _)) }
}
+/// # Examples
+/// ```
+/// use near_sdk::env::promise_batch_create;
+/// use near_sdk::AccountId;
+/// use std::str::FromStr;
+///
+/// let promise = promise_batch_create(
+/// &AccountId::from_str("receiver.near").unwrap()
+/// );
+/// ```
/// Create a NEAR promise which will have multiple promise actions inside.
///
/// Example:
@@ -586,6 +906,28 @@ pub fn promise_batch_create(account_id: &AccountId) -> PromiseIndex {
}
}
+/// # 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(
+/// 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)
+/// );
+///
+/// let new_promise = promise_batch_then(
+/// promise,
+/// &AccountId::from_str("receiver.near").unwrap()
+/// );
+/// ```
/// Attach a callback NEAR promise to a batch of NEAR promise actions.
///
/// More info about batching [here](crate::env::promise_batch_create)
@@ -603,6 +945,18 @@ pub fn promise_batch_then(promise_index: PromiseIndex, account_id: &AccountId) -
/// 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);
+/// ```
pub fn promise_batch_action_create_account(promise_index: PromiseIndex) {
unsafe { sys::promise_batch_action_create_account(promise_index.0) }
}
@@ -610,6 +964,19 @@ pub fn promise_batch_action_create_account(promise_index: PromiseIndex) {
/// 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);
+/// ```
pub fn promise_batch_action_deploy_contract(promise_index: PromiseIndex, code: &[u8]) {
unsafe {
sys::promise_batch_action_deploy_contract(
@@ -623,6 +990,25 @@ pub fn promise_batch_action_deploy_contract(promise_index: PromiseIndex, code: &
/// 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)
+/// );
+/// ```
pub fn promise_batch_action_function_call(
promise_index: PromiseIndex,
function_name: &str,
@@ -646,6 +1032,26 @@ pub fn promise_batch_action_function_call(
/// 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)
+/// );
+/// ```
pub fn promise_batch_action_function_call_weight(
promise_index: PromiseIndex,
function_name: &str,
@@ -671,6 +1077,21 @@ pub fn promise_batch_action_function_call_weight(
/// 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),
+/// );
+/// ```
pub fn promise_batch_action_transfer(promise_index: PromiseIndex, amount: NearToken) {
unsafe {
sys::promise_batch_action_transfer(
@@ -683,6 +1104,23 @@ pub fn promise_batch_action_transfer(promise_index: PromiseIndex, amount: NearTo
/// 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
+/// );
+/// ```
pub fn promise_batch_action_stake(
promise_index: PromiseIndex,
amount: NearToken,
@@ -701,6 +1139,25 @@ pub fn promise_batch_action_stake(
/// 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
+/// );
+/// ```
pub fn promise_batch_action_add_key_with_full_access(
promise_index: PromiseIndex,
public_key: &PublicKey,
@@ -723,6 +1180,27 @@ 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,
@@ -746,6 +1224,50 @@ pub fn promise_batch_action_add_key_with_function_call(
/// 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,
@@ -777,6 +1299,23 @@ pub fn promise_batch_action_add_key_allowance_with_function_call(
/// 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
+/// );
+/// ```
pub fn promise_batch_action_delete_key(promise_index: PromiseIndex, public_key: &PublicKey) {
unsafe {
sys::promise_batch_action_delete_key(
@@ -790,6 +1329,21 @@ pub fn promise_batch_action_delete_key(promise_index: PromiseIndex, public_key:
/// 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()
+/// );
+/// ```
pub fn promise_batch_action_delete_account(
promise_index: PromiseIndex,
beneficiary_id: &AccountId,
@@ -807,11 +1361,41 @@ pub fn promise_batch_action_delete_account(
/// 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);
+/// ```
pub fn promise_results_count() -> 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!");
+/// }
+/// };
+/// ```
pub fn promise_result(result_idx: u64) -> PromiseResult {
match promise_result_internal(result_idx) {
Ok(()) => {
@@ -829,8 +1413,29 @@ pub(crate) fn promise_result_internal(result_idx: u64) -> Result<(), PromiseErro
_ => 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);
+/// ```
pub fn promise_return(promise_idx: PromiseIndex) {
unsafe { sys::promise_return(promise_idx.0) }
}
@@ -845,6 +1450,42 @@ pub fn promise_return(promise_idx: PromiseIndex) {
///
/// 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()
+/// );
+/// ```
pub fn promise_yield_create(
function_name: &str,
arguments: &[u8],
@@ -873,6 +1514,42 @@ pub fn promise_yield_create(
/// 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()
+/// );
+/// ```
pub fn promise_yield_resume(data_id: &CryptoHash, data: &[u8]) -> bool {
unsafe {
sys::promise_yield_resume(
@@ -889,6 +1566,18 @@ pub fn promise_yield_resume(data_id: &CryptoHash, data: &[u8]) -> bool {
// ###############
/// 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 data = [0u8; size_of::()];
@@ -899,6 +1588,17 @@ pub fn validator_stake(account_id: &AccountId) -> NearToken {
}
/// 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 data = [0u8; size_of::()];
unsafe { sys::validator_total_stake(data.as_ptr() as u64) };
@@ -909,23 +1609,70 @@ pub fn validator_total_stake() -> NearToken {
// # 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()
+/// );
+/// ```
pub fn value_return(value: &[u8]) {
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")]
@@ -941,6 +1688,19 @@ pub fn abort() -> ! {
}
/// 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());
+/// ```
pub fn log_str(message: &str) {
#[cfg(all(debug_assertions, not(target_arch = "wasm32")))]
eprintln!("{}", message);
@@ -949,6 +1709,13 @@ pub fn log_str(message: &str) {
}
/// 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")))]
@@ -963,8 +1730,11 @@ pub fn log(message: &[u8]) {
/// Writes key-value into storage.
/// If another key-value existed in the storage with the same key it returns `true`, otherwise `false`.
///
-/// # Examples
+/// # 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};
///
@@ -989,8 +1759,11 @@ pub fn storage_write(key: &[u8], value: &[u8]) -> bool {
}
/// Reads the value stored under the given key.
///
-/// # Examples
+/// # 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};
///
@@ -1007,6 +1780,19 @@ pub fn storage_read(key: &[u8]) -> Option> {
}
/// 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);
+/// ```
pub fn storage_remove(key: &[u8]) -> bool {
match unsafe { sys::storage_remove(key.len() as _, key.as_ptr() as _, EVICTED_REGISTER) } {
0 => false,
@@ -1015,10 +1801,34 @@ pub fn storage_remove(key: &[u8]) -> bool {
}
}
/// 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,
@@ -1057,7 +1867,14 @@ pub fn state_exists() -> bool {
/// 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));
+/// ```
pub fn storage_byte_cost() -> NearToken {
NearToken::from_yoctonear(10_000_000_000_000_000_000u128)
}
@@ -1067,6 +1884,15 @@ pub fn storage_byte_cost() -> NearToken {
// ##################
/// 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
diff --git a/near-sdk/src/lib.rs b/near-sdk/src/lib.rs
index be9c7df54..d167cc010 100644
--- a/near-sdk/src/lib.rs
+++ b/near-sdk/src/lib.rs
@@ -95,6 +95,7 @@
// We want to enable all clippy lints, but some of them generate false positives.
#![allow(clippy::missing_const_for_fn, clippy::redundant_pub_crate)]
#![allow(clippy::multiple_bound_locations)]
+#![allow(clippy::needless_lifetimes)]
#[cfg(test)]
extern crate quickcheck;
diff --git a/near-sdk/src/store/mod.rs b/near-sdk/src/store/mod.rs
index f3909554e..1419f1ea5 100644
--- a/near-sdk/src/store/mod.rs
+++ b/near-sdk/src/store/mod.rs
@@ -20,8 +20,8 @@
//! If your collection has up to 100 entries, it's acceptable to use the native collection, as it might be simpler
//! since you don't have to manage prefixes as we do with near collections.
//! However, if your collection has 1,000 or more entries, it's better to use a near collection. The investigation
-//! mentioned above shows that running the contains method on a native HashSet consumes 41% more gas
-//! compared to a near IterableSet.
+//! mentioned above shows that running the contains method on a native [`std::collections::HashSet`] consumes 41% more gas
+//! compared to a near [`crate::store::IterableSet`].
//!
//! It's also a bad practice to have a native collection properties as a top level properties of your contract.
//! The contract will load all the properties before the contract method invocation. That means that all your native
@@ -52,7 +52,7 @@
//! - [`UnorderedMap`]: Storage version of [`std::collections::HashMap`]. No ordering
//! guarantees.
//!
-//! - [`TreeMap`](TreeMap) (`unstable`): Storage version of [`std::collections::BTreeMap`]. Ordered by key,
+//! - [`TreeMap`] (`unstable`): Storage version of [`std::collections::BTreeMap`]. Ordered by key,
//! which comes at the cost of more expensive lookups and iteration.
//!
//! Sets:
@@ -73,14 +73,10 @@
//!
//! More information about collections can be found in [NEAR documentation](https://docs.near.org/build/smart-contracts/anatomy/collections)
-#[cfg(feature = "unstable")]
mod lazy;
-#[cfg(feature = "unstable")]
pub use lazy::Lazy;
-#[cfg(feature = "unstable")]
mod lazy_option;
-#[cfg(feature = "unstable")]
pub use lazy_option::LazyOption;
pub mod vec;
diff --git a/near-sdk/src/test_utils/context.rs b/near-sdk/src/test_utils/context.rs
index 07766d6f4..45823aa69 100644
--- a/near-sdk/src/test_utils/context.rs
+++ b/near-sdk/src/test_utils/context.rs
@@ -65,7 +65,7 @@ pub struct VMContext {
pub random_seed: [u8; 32],
/// If Some, it means that execution is made in a view mode and defines its configuration.
/// View mode means that only read-only operations are allowed.
- /// See for more details.
+ /// See for more details.
pub view_config: Option,
/// How many `DataReceipt`'s should receive this execution result. This should be empty if
/// this function call is a part of a batch and it is not the last action.
diff --git a/near-sdk/src/test_utils/mod.rs b/near-sdk/src/test_utils/mod.rs
index 02f2fea2b..b305c0c98 100644
--- a/near-sdk/src/test_utils/mod.rs
+++ b/near-sdk/src/test_utils/mod.rs
@@ -19,7 +19,7 @@ pub use context::{accounts, testing_env_with_promise_results, VMContextBuilder};
/// about the VM to configure parameters not directly related to the transaction being executed.
/// - `fee_config`(optional): [`RuntimeFeesConfig`] which configures the
/// fees for execution and storage of transactions.
-/// - `validators`(optional): a [`HashMap`]<[`AccountId`], [`Balance`]> mocking the
+/// - `validators`(optional): a [`HashMap`]<[`AccountId`], [`NearToken`]> mocking the
/// current validators of the blockchain.
/// - `promise_results`(optional): a [`Vec`] of [`PromiseResult`] which mocks the results
/// of callback calls during the execution.
@@ -57,7 +57,7 @@ pub use context::{accounts, testing_env_with_promise_results, VMContextBuilder};
/// [`vm::Config`]: near_parameters::vm::Config
/// [`RuntimeFeesConfig`]: near_parameters::RuntimeFeesConfig
/// [`AccountId`]: crate::AccountId
-/// [`Balance`]: crate::Balance
+/// [`NearToken`]: crate::NearToken
/// [`PromiseResult`]: crate::PromiseResult
/// [`HashMap`]: std::collections::HashMap
#[macro_export]
diff --git a/near-sdk/src/test_utils/test_env.rs b/near-sdk/src/test_utils/test_env.rs
index 15b789fcd..670b40029 100644
--- a/near-sdk/src/test_utils/test_env.rs
+++ b/near-sdk/src/test_utils/test_env.rs
@@ -32,7 +32,7 @@ pub fn setup() {
}
/// free == effectively unlimited gas
-/// Sets up the blockchain interface with a [`VMConfig`] which sets the gas costs to zero.
+/// Sets up the blockchain interface with a [`near_parameters::vm::Config`] which sets the gas costs to zero.
pub fn setup_free() {
let mut config = test_vm_config();
config.make_free();
diff --git a/near-sdk/src/types/public_key.rs b/near-sdk/src/types/public_key.rs
index c66640765..8ce9f2f14 100644
--- a/near-sdk/src/types/public_key.rs
+++ b/near-sdk/src/types/public_key.rs
@@ -97,6 +97,7 @@ impl TryFrom for near_crypto::PublicKey {
/// .unwrap();
/// ```
#[derive(Debug, Clone, PartialEq, PartialOrd, Ord, Eq, BorshSerialize, Hash)]
+#[cfg_attr(feature = "abi", derive(borsh::BorshSchema))]
pub struct PublicKey {
data: Vec,
}
diff --git a/near-sdk/tests/store_performance_tests.rs b/near-sdk/tests/store_performance_tests.rs
index cb2a7e573..ff6d210de 100644
--- a/near-sdk/tests/store_performance_tests.rs
+++ b/near-sdk/tests/store_performance_tests.rs
@@ -9,6 +9,7 @@ use near_workspaces::types::{KeyType, SecretKey};
use near_workspaces::{Account, Worker};
use rand::Rng;
use serde::{Deserialize, Serialize};
+use std::fmt::Display;
use std::sync::Arc;
use strum_macros::Display;
@@ -27,6 +28,11 @@ pub enum Collection {
Vector,
}
+pub enum Contract {
+ StoreContract,
+ LazyContract,
+}
+
fn random_account_id(collection: Collection, seed: &str) -> AccountId {
let mut rng = rand::thread_rng();
let random_num = rng.gen_range(10000000000000usize..99999999999999);
@@ -53,16 +59,20 @@ async fn dev_generate(
Ok((account.into_result()?, collection))
}
-async fn setup_worker() -> anyhow::Result<(Arc>, AccountId)> {
+async fn setup_worker(contract: Contract) -> anyhow::Result<(Arc>, AccountId)> {
+ let contract_path = match contract {
+ Contract::StoreContract => "./tests/test-contracts/store",
+ Contract::LazyContract => "./tests/test-contracts/lazy",
+ };
let worker = Arc::new(near_workspaces::sandbox().await?);
- let wasm = near_workspaces::compile_project("./tests/test-contracts/store").await?;
+ let wasm = near_workspaces::compile_project(contract_path).await?;
let contract = worker.dev_deploy(&wasm).await?;
let res = contract.call("new").max_gas().transact().await?;
assert!(res.is_success());
Ok((worker, contract.id().clone()))
}
-fn perform_asserts(total_gas: u64, col: &Collection) {
+fn perform_asserts(total_gas: u64, col: impl Display, override_min_gas: Option) {
// Constraints a bit relaxed to account for binary differences due to on-demand compilation.
assert!(
total_gas < NearGas::from_tgas(110).as_gas(),
@@ -71,7 +81,7 @@ fn perform_asserts(total_gas: u64, col: &Collection) {
NearGas::from_gas(total_gas)
);
assert!(
- total_gas > NearGas::from_tgas(90).as_gas(),
+ total_gas > NearGas::from_tgas(override_min_gas.unwrap_or(90)).as_gas(),
"not enough gas consumed {}: {}, adjust the number of iterations to spot regressions",
col,
NearGas::from_gas(total_gas)
@@ -80,7 +90,7 @@ fn perform_asserts(total_gas: u64, col: &Collection) {
#[allow(unused)]
async fn setup_several(num: usize) -> anyhow::Result<(Vec, AccountId)> {
- let (worker, contract_id) = setup_worker().await?;
+ let (worker, contract_id) = setup_worker(Contract::StoreContract).await?;
let mut accounts = Vec::new();
for acc_seed in 0..num {
@@ -92,8 +102,8 @@ async fn setup_several(num: usize) -> anyhow::Result<(Vec, AccountId)>
Ok((accounts, contract_id))
}
-async fn setup() -> anyhow::Result<(Account, AccountId)> {
- let (worker, contract_id) = setup_worker().await?;
+async fn setup(contract: Contract) -> anyhow::Result<(Account, AccountId)> {
+ let (worker, contract_id) = setup_worker(contract).await?;
let (account, _) =
dev_generate(worker.clone(), Collection::IterableSet, "seed".to_string()).await?;
@@ -114,7 +124,7 @@ async fn insert_and_remove() -> anyhow::Result<()> {
Collection::Vector,
];
- let (account, contract_id) = setup().await?;
+ let (account, contract_id) = setup(Contract::StoreContract).await?;
// insert test, max_iterations here is the number of elements to insert. It's used to measure
// relative performance.
for (col, max_iterations) in collection_types.map(|col| match col {
@@ -137,7 +147,7 @@ async fn insert_and_remove() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, &col);
+ perform_asserts(total_gas, col, None);
}
// remove test, max_iterations here is the number of elements to remove. It's used to measure
@@ -162,7 +172,7 @@ async fn insert_and_remove() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, &col);
+ perform_asserts(total_gas, col, None);
}
Ok(())
@@ -181,7 +191,7 @@ async fn iter() -> anyhow::Result<()> {
];
let element_number = 100;
- let (account, contract_id) = setup().await?;
+ let (account, contract_id) = setup(Contract::StoreContract).await?;
// pre-populate
for col in collection_types {
@@ -215,7 +225,7 @@ async fn iter() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, &col);
+ perform_asserts(total_gas, col, None);
}
Ok(())
@@ -234,7 +244,7 @@ async fn random_access() -> anyhow::Result<()> {
Collection::Vector,
];
let element_number = 100;
- let (account, contract_id) = setup().await?;
+ let (account, contract_id) = setup(Contract::StoreContract).await?;
// pre-populate
for col in collection_types {
@@ -277,7 +287,7 @@ async fn random_access() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, &col);
+ perform_asserts(total_gas, col, None);
}
Ok(())
@@ -297,7 +307,7 @@ async fn contains() -> anyhow::Result<()> {
];
// Each collection gets the same number of elements.
let element_number = 100;
- let (account, contract_id) = setup().await?;
+ let (account, contract_id) = setup(Contract::StoreContract).await?;
// prepopulate
for col in collection_types {
@@ -332,7 +342,7 @@ async fn contains() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, &col);
+ perform_asserts(total_gas, col, None);
}
Ok(())
@@ -344,7 +354,7 @@ async fn contains() -> anyhow::Result<()> {
async fn iterable_vs_unordered() -> anyhow::Result<()> {
let element_number = 300;
let deleted_element_number = 299;
- let (account, contract_id) = setup().await?;
+ let (account, contract_id) = setup(Contract::StoreContract).await?;
// We only care about Unordered* and Iterable* collections.
let collection_types = &[
@@ -395,7 +405,7 @@ async fn iterable_vs_unordered() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, &col);
+ perform_asserts(total_gas, col, None);
}
// random access, repeat here is the number of times we try to access an element in the
@@ -417,8 +427,77 @@ async fn iterable_vs_unordered() -> anyhow::Result<()> {
.total_gas_burnt
.as_gas();
- perform_asserts(total_gas, col);
+ perform_asserts(total_gas, col, None);
}
Ok(())
}
+
+#[tokio::test]
+async fn test_lazy() -> anyhow::Result<()> {
+ let (account, contract_id) = setup(Contract::LazyContract).await?;
+
+ let res = account
+ .call(&contract_id, "insert_delete")
+ .args_json((700,))
+ .max_gas()
+ .transact()
+ .await?
+ .unwrap();
+
+ perform_asserts(res.total_gas_burnt.as_gas(), "lazy:insert_delete", None);
+
+ let res = account
+ .call(&contract_id, "insert_delete_flush_once")
+ .args_json((1700,))
+ .max_gas()
+ .transact()
+ .await?
+ .unwrap();
+
+ perform_asserts(res.total_gas_burnt.as_gas(), "lazy:insert_delete_flush_once", None);
+
+ let res = account
+ .call(&contract_id, "flush")
+ .args_json((2350000,))
+ .max_gas()
+ .transact()
+ .await?
+ .unwrap();
+
+ // Override min gas to avoid constant tuning, it's pretty clear this is performant. Somehow
+ // this is pretty flaky.
+ perform_asserts(res.total_gas_burnt.as_gas(), "lazy:flush", Some(60));
+
+ let res = account
+ .call(&contract_id, "get")
+ .args_json((2400000,))
+ .max_gas()
+ .transact()
+ .await?
+ .unwrap();
+
+ // Override min gas to avoid constant tuning, it's pretty clear this is performant.
+ perform_asserts(res.total_gas_burnt.as_gas(), "lazy:get", Some(70));
+
+ let res = account
+ .call(&contract_id, "insert_flush")
+ .args_json((1200,))
+ .max_gas()
+ .transact()
+ .await?
+ .unwrap();
+
+ perform_asserts(res.total_gas_burnt.as_gas(), "lazy:insert_flush", None);
+
+ let res = account
+ .call(&contract_id, "insert_take")
+ .args_json((700,))
+ .max_gas()
+ .transact()
+ .await?
+ .unwrap();
+
+ perform_asserts(res.total_gas_burnt.as_gas(), "lazy:insert_take", None);
+ Ok(())
+}
diff --git a/near-sdk/tests/test-contracts/lazy/Cargo.toml b/near-sdk/tests/test-contracts/lazy/Cargo.toml
new file mode 100644
index 000000000..42591aedc
--- /dev/null
+++ b/near-sdk/tests/test-contracts/lazy/Cargo.toml
@@ -0,0 +1,12 @@
+[package]
+name = "lazy"
+version = "0.1.0"
+edition = "2021"
+
+[lib]
+crate-type = ["cdylib"]
+
+[dependencies]
+near-sdk = { path = "../../../../near-sdk", features = ["default", "unstable"] }
+
+[workspace]
diff --git a/near-sdk/tests/test-contracts/lazy/src/lib.rs b/near-sdk/tests/test-contracts/lazy/src/lib.rs
new file mode 100644
index 000000000..6d2ac49a7
--- /dev/null
+++ b/near-sdk/tests/test-contracts/lazy/src/lib.rs
@@ -0,0 +1,96 @@
+use near_sdk::borsh::{BorshDeserialize, BorshSerialize};
+use near_sdk::{near, store::LazyOption, PanicOnDefault};
+
+#[derive(BorshSerialize, BorshDeserialize, Ord, PartialOrd, Eq, PartialEq, Clone)]
+#[borsh(crate = "near_sdk::borsh")]
+pub struct Insertable {
+ pub index: u32,
+ pub data: String,
+ pub is_valid: bool,
+}
+
+#[near(contract_state)]
+#[derive(PanicOnDefault)]
+pub struct LazyContract {
+ pub lazy_opt: LazyOption,
+}
+
+#[near]
+impl LazyContract {
+ #[init]
+ pub fn new() -> Self {
+ let lazy_opt = LazyOption::new(b"a", None);
+ Self { lazy_opt }
+ }
+
+ fn insertable(&self) -> Insertable {
+ Insertable { index: 0, data: "scatter cinnamon wheel useless please rough situate iron eager noise try evolve runway neglect onion".to_string(), is_valid: true }
+ }
+
+ /// This should only write to the underlying storage once.
+ #[payable]
+ pub fn flush(&mut self, iterations: usize) {
+ let insertable = self.insertable();
+ self.lazy_opt.set(Some(insertable));
+
+ for _ in 0..=iterations {
+ self.lazy_opt.flush();
+ }
+ }
+
+ #[payable]
+ pub fn get(&mut self, iterations: u32) {
+ let insertable = self.insertable();
+ self.lazy_opt.set(Some(insertable));
+ for _ in 0..=iterations {
+ self.lazy_opt.get();
+ }
+ }
+
+ /// This should write on each iteration.
+ #[payable]
+ pub fn insert_flush(&mut self, iterations: u32) {
+ let mut insertable = self.insertable();
+ for idx in 0..=iterations {
+ insertable.index = idx as u32;
+ self.lazy_opt.set(Some(insertable.clone()));
+ self.lazy_opt.flush();
+ }
+ }
+
+ /// This should write twice on each iteration.
+ #[payable]
+ pub fn insert_take(&mut self, iterations: u32) {
+ let mut insertable = self.insertable();
+ for idx in 0..=iterations {
+ insertable.index = idx as u32;
+ self.lazy_opt.set(Some(insertable.clone()));
+ self.lazy_opt.flush();
+ self.lazy_opt.take();
+ self.lazy_opt.flush();
+ }
+ }
+
+ /// This should write and delete on each iteration.
+ #[payable]
+ pub fn insert_delete(&mut self, iterations: u32) {
+ let insertable = self.insertable();
+ for _ in 0..=iterations {
+ self.lazy_opt.set(Some(insertable.clone()));
+ self.lazy_opt.flush();
+ self.lazy_opt.set(None);
+ self.lazy_opt.flush();
+ }
+ }
+
+ /// This should write once on each iteration.
+ #[payable]
+ pub fn insert_delete_flush_once(&mut self, iterations: u32) {
+ let insertable = self.insertable();
+ for _ in 0..=iterations {
+ self.lazy_opt.set(Some(insertable.clone()));
+ self.lazy_opt.set(None);
+ self.lazy_opt.flush();
+ }
+ }
+}