From 8a1c2da62ec67faa947343b10db7d1351a3d18f4 Mon Sep 17 00:00:00 2001 From: ymc182 Date: Tue, 5 Apr 2022 08:53:42 +1000 Subject: [PATCH 01/35] payout --- .../src/non_fungible_token/core/core_impl.rs | 12 ++- .../src/non_fungible_token/macros.rs | 40 ++++++++++ .../src/non_fungible_token/mod.rs | 3 + .../src/non_fungible_token/payout/mod.rs | 36 +++++++++ .../non_fungible_token/payout/payout_impl.rs | 80 +++++++++++++++++++ 5 files changed, 169 insertions(+), 2 deletions(-) create mode 100644 near-contract-standards/src/non_fungible_token/payout/mod.rs create mode 100644 near-contract-standards/src/non_fungible_token/payout/payout_impl.rs diff --git a/near-contract-standards/src/non_fungible_token/core/core_impl.rs b/near-contract-standards/src/non_fungible_token/core/core_impl.rs index 22f1c04e7..5a9cc8ed4 100644 --- a/near-contract-standards/src/non_fungible_token/core/core_impl.rs +++ b/near-contract-standards/src/non_fungible_token/core/core_impl.rs @@ -4,6 +4,7 @@ use crate::non_fungible_token::core::resolver::ext_nft_resolver; use crate::non_fungible_token::core::NonFungibleTokenCore; use crate::non_fungible_token::events::{NftMint, NftTransfer}; use crate::non_fungible_token::metadata::TokenMetadata; +use crate::non_fungible_token::payout::Royalties; use crate::non_fungible_token::token::{Token, TokenId}; use crate::non_fungible_token::utils::{refund_approved_account_ids, refund_deposit_to_account}; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; @@ -47,6 +48,7 @@ pub struct NonFungibleToken { // required by approval extension pub approvals_by_id: Option>>, pub next_approval_id_by_id: Option>, + pub royalties: Option, } #[derive(BorshStorageKey, BorshSerialize)] @@ -55,18 +57,20 @@ pub enum StorageKey { } impl NonFungibleToken { - pub fn new( + pub fn new( owner_by_id_prefix: Q, owner_id: AccountId, token_metadata_prefix: Option, enumeration_prefix: Option, approval_prefix: Option, + royalties_prefix: Option, ) -> Self where Q: IntoStorageKey, R: IntoStorageKey, S: IntoStorageKey, T: IntoStorageKey, + Y: IntoStorageKey, { let (approvals_by_id, next_approval_id_by_id) = if let Some(prefix) = approval_prefix { let prefix: Vec = prefix.into_storage_key(); @@ -86,6 +90,7 @@ impl NonFungibleToken { tokens_per_owner: enumeration_prefix.map(LookupMap::new), approvals_by_id, next_approval_id_by_id, + royalties: royalties_prefix.map(Royalties::new), }; this.measure_min_token_storage_cost(); this @@ -391,7 +396,10 @@ impl NonFungibleTokenCore for NonFungibleToken { msg: String, ) -> PromiseOrValue { assert_one_yocto(); - require!(env::prepaid_gas() > GAS_FOR_NFT_TRANSFER_CALL, "More gas is required"); + require!( + env::prepaid_gas() > GAS_FOR_NFT_TRANSFER_CALL + GAS_FOR_RESOLVE_TRANSFER, + "More gas is required" + ); let sender_id = env::predecessor_account_id(); let (old_owner, old_approvals) = self.internal_transfer(&sender_id, &receiver_id, &token_id, approval_id, memo); diff --git a/near-contract-standards/src/non_fungible_token/macros.rs b/near-contract-standards/src/non_fungible_token/macros.rs index ecd98045a..5cb572394 100644 --- a/near-contract-standards/src/non_fungible_token/macros.rs +++ b/near-contract-standards/src/non_fungible_token/macros.rs @@ -143,3 +143,43 @@ macro_rules! impl_non_fungible_token_enumeration { } }; } + +/// Non-fungible enumeration adds the extension standard offering several +/// view-only methods to get token supply, tokens per owner, etc. +#[macro_export] +macro_rules! impl_non_fungible_token_payout { + ($contract: ident, $token: ident) => { + use $crate::non_fungible_token::payout::NonFungibleTokenPayout; + + #[near_bindgen] + impl NonFungibleTokenPayout for $contract { + #[allow(unused_variables)] + fn nft_payout( + &self, + token_id: String, + balance: U128, + max_len_payout: Option, + ) -> Payout { + let owner_id = self.tokens.owner_by_id.get(&token_id).expect("No such token_id"); + self.royalties + .get() + .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) + } + #[payable] + fn nft_transfer_payout( + &mut self, + receiver_id: AccountId, + token_id: String, + approval_id: Option, + memo: Option, + balance: U128, + max_len_payout: Option, + ) -> Payout { + assert_one_yocto(); + let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); + self.nft_transfer(receiver_id, token_id, approval_id, memo); + payout + } + } + }; +} diff --git a/near-contract-standards/src/non_fungible_token/mod.rs b/near-contract-standards/src/non_fungible_token/mod.rs index 5bc8d4863..2cd2eb468 100644 --- a/near-contract-standards/src/non_fungible_token/mod.rs +++ b/near-contract-standards/src/non_fungible_token/mod.rs @@ -12,6 +12,9 @@ mod macros; /// Metadata traits and implementation according to the [NFT enumeration standard](https://nomicon.io/Standards/NonFungibleToken/Metadata.html). /// This covers both the contract metadata and the individual token metadata. pub mod metadata; + +pub mod payout; + /// The Token struct for the non-fungible token. mod token; pub use self::token::{Token, TokenId}; diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs new file mode 100644 index 000000000..2bab9faa5 --- /dev/null +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -0,0 +1,36 @@ +mod payout_impl; + +use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::json_types::U128; +use near_sdk::AccountId; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +#[derive(Default, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] +#[serde(crate = "near_sdk::serde")] + +pub struct Payout { + pub payout: HashMap, +} +#[derive(Deserialize, Serialize, BorshDeserialize, BorshSerialize, Default, Debug)] +#[serde(crate = "near_sdk::serde")] +pub struct Royalties { + key_prefix: Vec, + pub accounts: HashMap, + pub percent: u8, +} +/// Offers methods helpful in determining account ownership of NFTs and provides a way to page through NFTs per owner, determine total supply, etc. +pub trait NonFungibleTokenPayout { + fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option) -> Payout; + /// Given a `token_id` and NEAR-denominated balance, transfer the token + /// and return the `Payout` struct for the given token. Panic if the + /// length of the payout exceeds `max_len_payout.` + fn nft_transfer_payout( + &mut self, + receiver_id: AccountId, + token_id: String, + approval_id: Option, + memo: Option, + balance: U128, + max_len_payout: Option, + ) -> Payout; +} diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs new file mode 100644 index 000000000..17073a0f7 --- /dev/null +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -0,0 +1,80 @@ +use super::NonFungibleTokenPayout; +use crate::non_fungible_token::core::NonFungibleTokenCore; +use crate::non_fungible_token::payout::*; +use crate::non_fungible_token::NonFungibleToken; +use near_sdk::assert_one_yocto; +use near_sdk::{require, AccountId, Balance, IntoStorageKey}; +use std::collections::HashMap; +impl Royalties { + /* pub fn new(accounts: HashMap, percent: u8) -> Self { + let this = Self { accounts, percent }; + this.validate(); + this + } */ + pub fn new(key_prefix: S) -> Self + where + S: IntoStorageKey, + { + let temp_accounts: HashMap = HashMap::new(); + let this = + Self { key_prefix: key_prefix.into_storage_key(), accounts: temp_accounts, percent: 0 }; + this.validate(); + this + } + pub(crate) fn validate(&self) { + require!(self.percent <= 100, "royalty percent must be between 0 - 100"); + require!( + self.accounts.len() <= 10, + "can only have a maximum of 10 accounts spliting royalties" + ); + let mut total: u8 = 0; + self.accounts.iter().for_each(|(_, percent)| { + require!(*percent <= 100, "each royalty should be less than 100"); + total += percent; + }); + require!(total <= 100, "total percent of each royalty split must be less than 100") + } + pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { + let royalty_payment = apply_percent(self.percent, balance); + let mut payout = Payout { + payout: self + .accounts + .iter() + .map(|(account, percent)| { + (account.clone(), apply_percent(*percent, royalty_payment).into()) + }) + .collect(), + }; + let rest = balance - royalty_payment; + let owner_payout: u128 = payout.payout.get(owner_id).map_or(0, |x| x.0) + rest; + payout.payout.insert(owner_id.clone(), owner_payout.into()); + payout + } +} + +fn apply_percent(percent: u8, int: u128) -> u128 { + int * percent as u128 / 100u128 +} + +impl NonFungibleTokenPayout for NonFungibleToken { + #[allow(unused_variables)] + fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option) -> Payout { + let owner_id = self.owner_by_id.get(&token_id).expect("No such token_id"); + self.royalties.as_ref().map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) + } + + fn nft_transfer_payout( + &mut self, + receiver_id: AccountId, + token_id: String, + approval_id: Option, + memo: Option, + balance: U128, + max_len_payout: Option, + ) -> Payout { + assert_one_yocto(); + let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); + self.nft_transfer(receiver_id, token_id, approval_id, memo); + payout + } +} From da97136227cd9ebd89c1448f8f3d3f7425fc8b1e Mon Sep 17 00:00:00 2001 From: ymc182 Date: Sat, 26 Mar 2022 19:42:58 +1100 Subject: [PATCH 02/35] d --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index c8685f6db..2644cac22 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ target # Key pairs generated by the NEAR shell neardev **/.near-credentials - +near-contract-standards/src/near-sdk-sim .idea .vscode **/.DS_Store From 34c576fa39168dee13d64d0a95d5f980941d3211 Mon Sep 17 00:00:00 2001 From: ymc182 Date: Tue, 5 Apr 2022 09:05:24 +1000 Subject: [PATCH 03/35] included the sim --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 2644cac22..c8685f6db 100644 --- a/.gitignore +++ b/.gitignore @@ -13,7 +13,7 @@ target # Key pairs generated by the NEAR shell neardev **/.near-credentials -near-contract-standards/src/near-sdk-sim + .idea .vscode **/.DS_Store From 7b58745be45a816ae903da2a8825e4485e2bacc1 Mon Sep 17 00:00:00 2001 From: ymc182 Date: Tue, 5 Apr 2022 09:05:34 +1000 Subject: [PATCH 04/35] sim --- Cargo.toml | 1 + near-sdk-sim/Cargo.lock | 1987 +++++++++++++++++++++++++++++++++++ near-sdk-sim/Cargo.toml | 38 + near-sdk-sim/README.md | 472 +++++++++ near-sdk-sim/src/cache.rs | 91 ++ near-sdk-sim/src/lib.rs | 24 + near-sdk-sim/src/outcome.rs | 259 +++++ near-sdk-sim/src/runtime.rs | 548 ++++++++++ near-sdk-sim/src/units.rs | 20 + near-sdk-sim/src/user.rs | 555 ++++++++++ 10 files changed, 3995 insertions(+) create mode 100644 near-sdk-sim/Cargo.lock create mode 100644 near-sdk-sim/Cargo.toml create mode 100644 near-sdk-sim/README.md create mode 100644 near-sdk-sim/src/cache.rs create mode 100644 near-sdk-sim/src/lib.rs create mode 100644 near-sdk-sim/src/outcome.rs create mode 100644 near-sdk-sim/src/runtime.rs create mode 100644 near-sdk-sim/src/units.rs create mode 100644 near-sdk-sim/src/user.rs diff --git a/Cargo.toml b/Cargo.toml index 17f9fc680..6a7e4184b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,7 @@ members = [ "near-sdk", "near-sdk-macros", + "near-sdk-sim", "near-contract-standards", "sys", ] diff --git a/near-sdk-sim/Cargo.lock b/near-sdk-sim/Cargo.lock new file mode 100644 index 000000000..50ec6a1c7 --- /dev/null +++ b/near-sdk-sim/Cargo.lock @@ -0,0 +1,1987 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "Inflector" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" + +[[package]] +name = "aho-corasick" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" +dependencies = [ + "memchr", +] + +[[package]] +name = "ansi_term" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" +dependencies = [ + "winapi", +] + +[[package]] +name = "arrayref" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" + +[[package]] +name = "arrayvec" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" + +[[package]] +name = "atty" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] + +[[package]] +name = "autocfg" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" + +[[package]] +name = "base64" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" + +[[package]] +name = "bincode" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "bindgen" +version = "0.54.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66c0bb6167449588ff70803f4127f0684f9063097eca5016f37eb52b92c2cf36" +dependencies = [ + "bitflags", + "cexpr", + "cfg-if", + "clang-sys", + "clap", + "env_logger", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "which", +] + +[[package]] +name = "bitflags" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" + +[[package]] +name = "blake2" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" +dependencies = [ + "byte-tools", + "crypto-mac 0.7.0", + "digest 0.8.1", + "opaque-debug", +] + +[[package]] +name = "blake3" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce4f9586c9a3151c4b49b19e82ba163dd073614dd057e53c969e1a4db5b52720" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "crypto-mac 0.8.0", + "digest 0.9.0", +] + +[[package]] +name = "block-buffer" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" +dependencies = [ + "block-padding", + "byte-tools", + "byteorder", + "generic-array 0.12.3", +] + +[[package]] +name = "block-padding" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" +dependencies = [ + "byte-tools", +] + +[[package]] +name = "borsh" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7769f8f6fdc6ac7617bbc8bc7ef9dc263cd459d99d21cf2ab4afc3bc8d7d70d" +dependencies = [ + "borsh-derive 0.6.2", +] + +[[package]] +name = "borsh" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81d42092adf8d207d987cb8d676f068305a80b3dd58487a06680e8586e7c4c25" +dependencies = [ + "borsh-derive 0.7.0", +] + +[[package]] +name = "borsh-derive" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2689a82a5fe57f9e71997b16bea340da338c7fb8db400b8d9d55b59010540d8" +dependencies = [ + "borsh-derive-internal 0.6.2", + "borsh-schema-derive-internal 0.6.2", + "syn", +] + +[[package]] +name = "borsh-derive" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "912b5e5f545801db1290ea4f05c8af512db0805dade9b9af76798c5fc4a04164" +dependencies = [ + "borsh-derive-internal 0.7.0", + "borsh-schema-derive-internal 0.7.0", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39b621f19e9891a34f679034fa2238260e27c0eddfe2804e9fb282061cf9b294" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-derive-internal" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e75ae71ba613fe0b86556e910b053b040cb52b8c52c034370744f8760f176262" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "befebdb9e223ae4528b3d597dbbfb5c68566822d2a3de3e260f235360773ba29" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "borsh-schema-derive-internal" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1de62f1b5fe76adc6510a4e7c3feb0172650f0088bbd5fead41c0c016db5c5ae" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "bs58" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" + +[[package]] +name = "byte-tools" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" + +[[package]] +name = "byteorder" +version = "1.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" + +[[package]] +name = "c2-chacha" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217192c943108d8b13bac38a1d51df9ce8a407a3f5a71ab633980665e68fbd9a" +dependencies = [ + "byteorder", + "ppv-lite86", + "stream-cipher", +] + +[[package]] +name = "cached" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "083dc50149e37dfaab1ffe2c3af03576b79550f1e419a5091b28a7191db1c45b" +dependencies = [ + "once_cell", +] + +[[package]] +name = "cc" +version = "1.0.59" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" +dependencies = [ + "jobserver", +] + +[[package]] +name = "cexpr" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "chrono" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" +dependencies = [ + "num-integer", + "num-traits", + "serde", + "time", +] + +[[package]] +name = "clang-sys" +version = "0.29.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "2.33.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" +dependencies = [ + "ansi_term", + "atty", + "bitflags", + "strsim", + "textwrap", + "unicode-width", + "vec_map", +] + +[[package]] +name = "clear_on_drop" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9cc5db465b294c3fa986d5bbb0f3017cd850bff6dd6c52f9ccff8b4d21b7b08" +dependencies = [ + "cc", +] + +[[package]] +name = "cloudabi" +version = "0.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" +dependencies = [ + "bitflags", +] + +[[package]] +name = "constant_time_eq" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "crypto-mac" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" +dependencies = [ + "generic-array 0.12.3", + "subtle 1.0.0", +] + +[[package]] +name = "crypto-mac" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" +dependencies = [ + "generic-array 0.14.4", + "subtle 2.2.3", +] + +[[package]] +name = "curve25519-dalek" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" +dependencies = [ + "byteorder", + "digest 0.8.1", + "rand_core", + "subtle 2.2.3", + "zeroize", +] + +[[package]] +name = "derive_more" +version = "0.99.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "digest" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "digest" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" +dependencies = [ + "generic-array 0.14.4", +] + +[[package]] +name = "dynasm" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42a814e1edeb85dd2a3c6fc0d6bf76d02ca5695d438c70ecee3d90774f3259c5" +dependencies = [ + "bitflags", + "byteorder", + "lazy_static", + "owning_ref", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "dynasmrt" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a393aaeb4441a48bcf47b5b6155971f82cc1eb77e22855403ccc0415ac8328d" +dependencies = [ + "byteorder", + "memmap", +] + +[[package]] +name = "easy-ext" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73bb6373ab18cda357d060e1cc36ca2f3fc2783a81b033087a55ac2829cfa737" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ed25519-dalek" +version = "1.0.0-pre.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" +dependencies = [ + "clear_on_drop", + "curve25519-dalek", + "rand", + "sha2", +] + +[[package]] +name = "elastic-array" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d63720ea2bc2e1b79f7aa044d9dc0b825f9ccb6930b32120f8fb9e873aa84bc" +dependencies = [ + "heapsize", +] + +[[package]] +name = "env_logger" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] + +[[package]] +name = "errno" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" +dependencies = [ + "errno-dragonfly", + "libc", + "winapi", +] + +[[package]] +name = "errno-dragonfly" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" +dependencies = [ + "gcc", + "libc", +] + +[[package]] +name = "fake-simd" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" + +[[package]] +name = "fixed-hash" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c" +dependencies = [ + "static_assertions", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "fs_extra" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "generic-array" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" +dependencies = [ + "typenum", +] + +[[package]] +name = "generic-array" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "glob" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" + +[[package]] +name = "hashbrown" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" +dependencies = [ + "autocfg", +] + +[[package]] +name = "heapsize" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" +dependencies = [ + "winapi", +] + +[[package]] +name = "heck" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" +dependencies = [ + "unicode-segmentation", +] + +[[package]] +name = "hermit-abi" +version = "0.1.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" +dependencies = [ + "libc", +] + +[[package]] +name = "hex" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" + +[[package]] +name = "humantime" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" +dependencies = [ + "quick-error", +] + +[[package]] +name = "idna" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + +[[package]] +name = "if_chain" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d" + +[[package]] +name = "indexmap" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" +dependencies = [ + "autocfg", + "hashbrown", + "serde", +] + +[[package]] +name = "itoa" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" + +[[package]] +name = "jemalloc-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" +dependencies = [ + "cc", + "fs_extra", + "libc", +] + +[[package]] +name = "jemallocator" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69" +dependencies = [ + "jemalloc-sys", + "libc", +] + +[[package]] +name = "jobserver" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" +dependencies = [ + "libc", +] + +[[package]] +name = "keccak" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "lazycell" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" + +[[package]] +name = "libc" +version = "0.2.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" + +[[package]] +name = "libloading" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" +dependencies = [ + "cc", + "winapi", +] + +[[package]] +name = "librocksdb-sys" +version = "6.10.2" +source = "git+https://github.com/nearprotocol/rust-rocksdb?branch=disable-thread#44592812fd12cc2ea5de55af7c8d9bfdeacac692" +dependencies = [ + "bindgen", + "cc", + "glob", + "libc", +] + +[[package]] +name = "lock_api" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "matches" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" + +[[package]] +name = "memchr" +version = "2.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "near-crypto" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "arrayref", + "blake2", + "borsh 0.7.0", + "bs58", + "c2-chacha", + "curve25519-dalek", + "digest 0.8.1", + "ed25519-dalek", + "lazy_static", + "libc", + "parity-secp256k1", + "rand", + "rand_core", + "serde", + "serde_json", + "sha2", + "subtle 2.2.3", +] + +[[package]] +name = "near-metrics" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "lazy_static", + "log", + "prometheus", +] + +[[package]] +name = "near-pool" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "borsh 0.7.0", + "near-crypto", + "near-primitives", + "rand", +] + +[[package]] +name = "near-primitives" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "base64", + "borsh 0.7.0", + "bs58", + "byteorder", + "chrono", + "derive_more", + "easy-ext", + "hex", + "jemallocator", + "lazy_static", + "near-crypto", + "near-rpc-error-macro 0.1.0 (git+https://github.com/nearprotocol/nearcore.git)", + "near-vm-errors 1.2.0", + "num-rational", + "primitive-types", + "rand", + "reed-solomon-erasure", + "regex", + "serde", + "serde_json", + "sha2", + "smart-default", + "validator", + "validator_derive", +] + +[[package]] +name = "near-rpc-error-core" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn", +] + +[[package]] +name = "near-rpc-error-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffa8dbf8437a28ac40fcb85859ab0d0b8385013935b000c7a51ae79631dd74d9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-rpc-error-macro" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "near-rpc-error-core 0.1.0 (git+https://github.com/nearprotocol/nearcore.git)", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-rpc-error-macro" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6111d713e90c7c551dee937f4a06cb9ea2672243455a4454cc7566387ba2d9" +dependencies = [ + "near-rpc-error-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2", + "quote", + "serde", + "serde_json", + "syn", +] + +[[package]] +name = "near-runtime-configs" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "near-primitives", + "near-runtime-fees 1.2.0", + "near-vm-logic 1.2.0", + "serde", +] + +[[package]] +name = "near-runtime-fees" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f4992274c8acb33fa1246715d3aafbce5688ae82243c779b561f8eaff1bb6f1" +dependencies = [ + "num-rational", + "serde", +] + +[[package]] +name = "near-runtime-fees" +version = "1.2.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "num-rational", + "serde", +] + +[[package]] +name = "near-runtime-standalone" +version = "1.2.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "near-crypto", + "near-pool", + "near-primitives", + "near-runtime-configs", + "near-store", + "node-runtime", +] + +[[package]] +name = "near-sdk" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81319d4d44283f63467e4f02b6209297b10643c7aeb62e2ee41e0c31b43e2375" +dependencies = [ + "base64", + "borsh 0.6.2", + "bs58", + "near-runtime-fees 0.9.1", + "near-sdk-macros", + "near-vm-logic 0.9.1", + "serde", +] + +[[package]] +name = "near-sdk-core" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3767fc2a61e6577f1336e06d6962a6c61fc39299573b8a25696fd09ce96ffb" +dependencies = [ + "Inflector", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "near-sdk-macros" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27c06b45c56028b0e1241b2196397d449091665f3f08d543415373505df5e05f" +dependencies = [ + "near-sdk-core", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "near-store" +version = "0.1.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "borsh 0.7.0", + "byteorder", + "cached", + "derive_more", + "elastic-array", + "lazy_static", + "near-crypto", + "near-primitives", + "num_cpus", + "rand", + "rocksdb", + "serde", + "serde_json", + "strum", + "strum_macros", +] + +[[package]] +name = "near-test" +version = "0.1.0" +dependencies = [ + "borsh 0.6.2", + "env_logger", + "log", + "near-crypto", + "near-primitives", + "near-runtime-standalone", + "near-sdk", + "quickcheck", + "quickcheck_macros", + "serde", + "serde_json", +] + +[[package]] +name = "near-vm-errors" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "386c2c07ef37ae52ad43860ef69c6322bbc1e610ae0c08c1d7f5ff56f4c28e6a" +dependencies = [ + "borsh 0.6.2", + "near-rpc-error-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde", +] + +[[package]] +name = "near-vm-errors" +version = "1.2.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "borsh 0.7.0", + "near-rpc-error-macro 0.1.0 (git+https://github.com/nearprotocol/nearcore.git)", + "serde", +] + +[[package]] +name = "near-vm-logic" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6da6c80d3428f45248577820bfc943b8261a6f11d6721037e5c3f43484047cd" +dependencies = [ + "base64", + "bs58", + "byteorder", + "near-runtime-fees 0.9.1", + "near-vm-errors 0.9.1", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "near-vm-logic" +version = "1.2.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "base64", + "bs58", + "byteorder", + "near-runtime-fees 1.2.0", + "near-vm-errors 1.2.0", + "serde", + "sha2", + "sha3", +] + +[[package]] +name = "near-vm-runner" +version = "1.2.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "cached", + "near-runtime-fees 1.2.0", + "near-vm-errors 1.2.0", + "near-vm-logic 1.2.0", + "parity-wasm", + "pwasm-utils", + "wasmer-runtime", + "wasmer-runtime-core", +] + +[[package]] +name = "nix" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" +dependencies = [ + "bitflags", + "cc", + "cfg-if", + "libc", + "void", +] + +[[package]] +name = "node-runtime" +version = "1.2.0" +source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" +dependencies = [ + "borsh 0.7.0", + "byteorder", + "cached", + "lazy_static", + "log", + "near-crypto", + "near-metrics", + "near-primitives", + "near-runtime-configs", + "near-runtime-fees 1.2.0", + "near-store", + "near-vm-errors 1.2.0", + "near-vm-logic 1.2.0", + "near-vm-runner", + "num-bigint", + "num-rational", + "num-traits", + "rand", + "rocksdb", + "serde", + "serde_json", +] + +[[package]] +name = "nom" +version = "5.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" +dependencies = [ + "memchr", + "version_check", +] + +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" +dependencies = [ + "autocfg", + "num-bigint", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-traits" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "once_cell" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" + +[[package]] +name = "opaque-debug" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" + +[[package]] +name = "owning_ref" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "page_size" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "parity-secp256k1" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" +dependencies = [ + "arrayvec", + "cc", + "cfg-if", + "rand", +] + +[[package]] +name = "parity-wasm" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" + +[[package]] +name = "parking_lot" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" +dependencies = [ + "cfg-if", + "cloudabi", + "libc", + "redox_syscall", + "smallvec", + "winapi", +] + +[[package]] +name = "peeking_take_while" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" + +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "ppv-lite86" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" + +[[package]] +name = "primitive-types" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8" +dependencies = [ + "fixed-hash", + "uint", +] + +[[package]] +name = "proc-macro2" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" +dependencies = [ + "unicode-xid", +] + +[[package]] +name = "prometheus" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0575e258dab62268e7236d7307caa38848acbda7ec7ab87bd9093791e999d20" +dependencies = [ + "cfg-if", + "fnv", + "lazy_static", + "protobuf", + "spin", + "thiserror", +] + +[[package]] +name = "protobuf" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb14183cc7f213ee2410067e1ceeadba2a7478a59432ff0747a335202798b1e2" + +[[package]] +name = "pwasm-utils" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" +dependencies = [ + "byteorder", + "log", + "parity-wasm", +] + +[[package]] +name = "quick-error" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" + +[[package]] +name = "quickcheck" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" +dependencies = [ + "env_logger", + "log", + "rand", + "rand_core", +] + +[[package]] +name = "quickcheck_macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "quote" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" +dependencies = [ + "getrandom", + "libc", + "rand_chacha", + "rand_core", + "rand_hc", +] + +[[package]] +name = "rand_chacha" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rand_hc" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" +dependencies = [ + "rand_core", +] + +[[package]] +name = "redox_syscall" +version = "0.1.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" + +[[package]] +name = "reed-solomon-erasure" +version = "4.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" +dependencies = [ + "smallvec", +] + +[[package]] +name = "regex" +version = "1.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", + "thread_local", +] + +[[package]] +name = "regex-syntax" +version = "0.6.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" + +[[package]] +name = "rocksdb" +version = "0.14.0" +source = "git+https://github.com/nearprotocol/rust-rocksdb?branch=disable-thread#44592812fd12cc2ea5de55af7c8d9bfdeacac692" +dependencies = [ + "libc", + "librocksdb-sys", +] + +[[package]] +name = "rustc-hash" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" + +[[package]] +name = "rustc-hex" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "ryu" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" + +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "serde" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde-bench" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" +dependencies = [ + "byteorder", + "serde", +] + +[[package]] +name = "serde_bytes" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_derive" +version = "1.0.115" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.57" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" +dependencies = [ + "block-buffer", + "digest 0.8.1", + "fake-simd", + "opaque-debug", +] + +[[package]] +name = "sha3" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" +dependencies = [ + "block-buffer", + "byte-tools", + "digest 0.8.1", + "keccak", + "opaque-debug", +] + +[[package]] +name = "shlex" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" + +[[package]] +name = "smallvec" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" + +[[package]] +name = "smart-default" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "stream-cipher" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" +dependencies = [ + "generic-array 0.12.3", +] + +[[package]] +name = "strsim" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" + +[[package]] +name = "strum" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" + +[[package]] +name = "strum_macros" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "subtle" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" + +[[package]] +name = "subtle" +version = "2.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" + +[[package]] +name = "syn" +version = "1.0.39" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-xid", +] + +[[package]] +name = "target-lexicon" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" + +[[package]] +name = "termcolor" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "textwrap" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" +dependencies = [ + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "thread_local" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "time" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "tinyvec" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" + +[[package]] +name = "typenum" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" + +[[package]] +name = "uint" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" +dependencies = [ + "byteorder", + "crunchy", + "rustc-hex", + "static_assertions", +] + +[[package]] +name = "unicode-bidi" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" +dependencies = [ + "matches", +] + +[[package]] +name = "unicode-normalization" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "unicode-segmentation" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" + +[[package]] +name = "unicode-width" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" + +[[package]] +name = "unicode-xid" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" + +[[package]] +name = "url" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" +dependencies = [ + "idna", + "matches", + "percent-encoding", +] + +[[package]] +name = "validator" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e60fadf92c22236de4028ceb0b8af50ed3430d41ad43d7a7d63b6bd1a8f47c38" +dependencies = [ + "idna", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", +] + +[[package]] +name = "validator_derive" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d577dfb8ca9440a5c0b053d5a19b68f5c92ef57064bac87c8205c3f6072c20f" +dependencies = [ + "if_chain", + "lazy_static", + "proc-macro2", + "quote", + "regex", + "syn", + "validator", +] + +[[package]] +name = "vec_map" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" + +[[package]] +name = "version_check" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" + +[[package]] +name = "void" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "wasi" +version = "0.9.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" + +[[package]] +name = "wasmer-runtime" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92a9ae96b193c35c47fc829265198322cf980edc353a9de32bc87a1545d44f3" +dependencies = [ + "lazy_static", + "memmap", + "serde", + "serde_derive", + "wasmer-runtime-core", + "wasmer-singlepass-backend", +] + +[[package]] +name = "wasmer-runtime-core" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740161245998752cf1a567e860fd6355df0336fedca6be1940ec7aaa59643220" +dependencies = [ + "bincode", + "blake3", + "cc", + "digest 0.8.1", + "errno", + "hex", + "indexmap", + "lazy_static", + "libc", + "nix", + "page_size", + "parking_lot", + "rustc_version", + "serde", + "serde-bench", + "serde_bytes", + "serde_derive", + "smallvec", + "target-lexicon", + "wasmparser", + "winapi", +] + +[[package]] +name = "wasmer-singlepass-backend" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a434ebf512ae1c76de46d41935a9ae10775fd937071334bdf5878cc41d9140" +dependencies = [ + "bincode", + "byteorder", + "dynasm", + "dynasmrt", + "lazy_static", + "libc", + "nix", + "serde", + "serde_derive", + "smallvec", + "wasmer-runtime-core", +] + +[[package]] +name = "wasmparser" +version = "0.51.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" + +[[package]] +name = "which" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" +dependencies = [ + "libc", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" +dependencies = [ + "winapi", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "zeroize" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" diff --git a/near-sdk-sim/Cargo.toml b/near-sdk-sim/Cargo.toml new file mode 100644 index 000000000..198fc96d0 --- /dev/null +++ b/near-sdk-sim/Cargo.toml @@ -0,0 +1,38 @@ +[package] +name = "near-sdk-sim" +version = "4.0.0-pre.7" +authors = ["Near Inc "] +edition = "2018" +license = "GPL-3.0" +categories = ["wasm"] +repository = "https://github.com/near/near-sdk-rs" +homepage = "https://near-sdk.io" +description = """ +NEAR Simulator & cross-contract testing library +""" + + +[dependencies] +near-sdk = { path = "../near-sdk", version = "=4.0.0-pre.7" } +near-crypto = "=0.1.0" +near-primitives = "=0.1.0-pre.1" +near-vm-logic = "=4.0.0-pre.1" + +near-pool = "=0.1.0-pre.1" +near-store = "=0.1.0-pre.1" +near-runtime = "=4.0.0-pre.1" + +lazy-static-include = "3" +# Temporary workaround see https://github.com/bitvecto-rs/bitvec/issues/105 +funty = "=1.1.0" + +[dev-dependencies] +quickcheck = "0.9" +quickcheck_macros = "0.9" +fungible-token = { path="../examples/fungible-token/ft" } + +[features] +default = [] +no_cache = ["near-store/no_cache", "near-runtime/no_cache"] +no_sim = [] +no_contract_cache = [] diff --git a/near-sdk-sim/README.md b/near-sdk-sim/README.md new file mode 100644 index 000000000..9e6b48c4f --- /dev/null +++ b/near-sdk-sim/README.md @@ -0,0 +1,472 @@ +# NEAR Simulator & cross-contract testing library + +When writing NEAR contracts, with Rust or other Wasm-compiled languages like [AssemblyScript](https://github.com/near/near-sdk-as), the default testing approach for your language of choice (such as `mod test` in your Rust project's `src/lib.rs` file) is great for testing the behavior of a single contract in isolation. + +But the true power of blockchains & smart contracts comes from cross-contract calls. How do you make sure your cross-contract code works as you expect? + +As a first step, you can use this library! With it, you can: + +* Test cross-contract calls +* Profile [gas](https://docs.near.org/docs/concepts/gas) & [storage](https://docs.near.org/docs/concepts/storage-staking) usage for your contract, establishing lower bounds for costs of deployed contracts and rapidly identifying problematic areas prior to deploying. +* Inspect intermediate state of all calls in a complicated chain of transactions + +To view this documentation locally, clone this repo and from this folder run `cargo doc --open`. + +## Changelog + +### `3.2.0` + +* Introduce `block_prod_time` duration in nanoseconds to `GenesisConfig` that defines the duration between produced blocks. +* Expose `cur_block` and `genesis_config` from `RuntimeStandalone`. This allows to manipulate block time. +* Use `RuntimeConfig::from_protocol_version` that fixes storage costs issue. +* Set root account balance to one billion tokens. + +# Getting started + +This section will guide you through our suggested approach to adding simulation tests to your project. Want an example? Check out the [Fungible Token Example](https://github.com/near/near-sdk-rs/tree/master/examples/fungible-token). + +## Dependency versions + +Currently this crate depends on a the GitHub repo of [nearcore](https://github.com/near/nearcore), so this crate must be a git dependency too. Furthermore, this crate's dependencies conflict with building the Wasm smart contract, so you must add it under the following: + +```toml +[dev-dependencies] +near-sdk-sim = "4.0.0-pre.6" + +``` + +And update `near-sdk` too: + +```toml +[dependencies] +near-sdk = "4.0.0-pre.6" + +``` + +Note that you need to add the `tag` (or `commit`) of the version. + + +## Workspace setup + +If you want to check gas & storage usage of one Rust contract, you can add the above dependencies to `Cargo.toml` in the root of your project. If you want to test cross-contract calls, we recommend setting up a cargo [workspace](https://doc.rust-lang.org/cargo/reference/workspaces.html). Here's how it works: + +Let's say you have an existing contract project in a folder called `contract`. + +Go ahead and make a subfolder within it called `contract`, and move the original contents of `contract` into this subfolder. Now you'll have `contract/contract`. You can rename the root folder to something like `contracts` or `contract-wrap`, if you want. Some bash commands to do this: + +```bash +mkdir contract-wrap +mv contract contract-wrap +``` + +Now in the root of the project (`contract-wrap`), create a new `Cargo.toml`. You'll have to add the normal `[package]` section, but unlike most projects you won't have any `dependencies`, only `dev-dependencies` and a `workspace`: + + +```toml +[dev-dependencies] +near-sdk = "4.0.0-pre.6" +near-sdk-sim = "4.0.0-pre.6" +contract = { path = "./contract" } + +[workspace] +members = [ + "contract" +] +``` + +Now when you want to create test contracts, you can add a new subfolder to your project and add a line for it to both `[dev-dependencies]` and `[workspace]` in this root `Cargo.toml`. + +Other cleanup: + +* You can move any `[profile.release]` settings from your nested project up to the root of the workspace, since workspace members inherit these settings from the workspace root. +* You can remove the nested project's `target`, since all workspace members will be built to the root project's `target` directory +* If you were building with `cargo build`, you can now build all workspace members at once with `cargo build --all` + + +## Test files + +In the root of your project (`contract-wrap` in the example above), create a `tests` directory with a Rust file inside. Anything in here will automatically be run by `cargo test`. + +Inside this folder, set up a new test crate for yourself by creating a `tests/sim` directory with a `tests/sim/main.rs` file. This file will glue together the other files (aka modules) in this folder. We'll add things to it soon. For now you can leave it empty. + +Now create a `tests/sim/utils.rs` file. This file will export common functions for all your tests. In it you need to include the bytes of the contract(s) you want to test: + +```rust +near_sdk_sim::lazy_static_include::lazy_static_include_bytes! { + // update `contract.wasm` for your contract's name + CONTRACT_WASM_BYTES => "target/wasm32-unknown-unknown/release/contract.wasm", + + // if you run `cargo build` without `--release` flag: + CONTRACT_WASM_BYTES => "target/wasm32-unknown-unknown/debug/contract.wasm", +} +``` + +Note that this means **you must `build` before you `test`!** Since `cargo test` does not re-generate the `wasm` files that your simulation tests rely on, you will need to `cargo build --all --target wasm32-unknown-unknown` before running `cargo test`. If you made contract changes and _you swear it should pass now_, try rebuilding! + +Now you can make a function to initialize your simulator: + +```rust +use near_sdk_sim::{init_simulator, to_yocto, STORAGE_AMOUNT}; + +const CONTRACT_ID: &str = "contract"; + +pub fn init() -> (UserAccount, UserAccount, UserAccount) { + // Use `None` for default genesis configuration; more info below + let root = init_simulator(None); + + let contract = root.deploy( + &CONTRACT_WASM_BYTES, + CONTRACT_ID.to_string(), + STORAGE_AMOUNT // attached deposit + ); + + let alice = root.create_user( + "alice".parse().unwrap(), + to_yocto("100") // initial balance + ); + + (root, contract, alice) +} +``` + +Now you can add a test file that uses this `init` function in `tests/sim/first_tests.rs`. For every file you add to this directory, you'll need to add a line to `tests/sim/main.rs`. Let's add one for both files so far: + +```rust +// in tests/sim/main.rs +mod utils; +mod first_tests; +``` + +Now add some tests to `first_tests.rs`: + +```rust +use near_sdk::serde_json::json; +use near_sdk_sim::DEFAULT_GAS; + +use crate::utils::init; + +#[test] +fn simulate_some_view_function() { + let (root, contract, _alice) = init(); + + let actual: String = root.view( + contract.account_id(), + "view_something", + &json!({ + "some_param": "some_value".to_string(), + }).to_string().into_bytes(), + ).unwrap_json(); + + assert_eq!("expected".to_string(), actual); +} + +#[test] +fn simulate_some_change_method() { + let (root, contract, _alice) = init(); + + let result = root.call( + contract.account_id(), + "change_something", + json!({ + "some_param": "some_value".to_string(), + }).to_string().into_bytes(), + DEFAULT_GAS, + 1, // deposit + ); + + assert!(result.is_ok()); +} +``` + + +## Optional macros + +The above approach is a good start, and will work even if your Wasm files are compiled from a language other than Rust. + +But if your original files are Rust and you want better ergonomics while testing, `near-sdk-sim` provides a nice bonus feature. + +`near-sdk-sim` modifies the `near_bindgen` macro from `near-sdk` to create an additional struct+implementation from your contract, with `Contract` added to the end of the name, like `xxxxxContract`. So if you have a contract with `[package].name` set to `token` with this in its `src/lib.rs`: + +```rust +#[near_bindgen] +struct Token { + ... +} + +#[near_bindgen] +impl Token { + ... +} +``` + +Then in your simulation test you can import `TokenContract`: + +```rust +use token::TokenContract; + +// or rename it maybe +use token::TokenContract as OtherNamedContract; +``` + +Now you can simplify the `init` & test code from the previous section: + +```rust +// in utils.rs +use near_sdk_sim::{deploy, init_simulator, to_yocto, STORAGE_AMOUNT}; +use token::TokenContract; + +const CONTRACT_ID: &str = "contract"; + +pub fn init() -> (UserAccount, ContractAccount, UserAccount) { + let root = init_simulator(None); + + let contract = deploy!( + contract: TokenContract, + contract_id: CONTRACT_ID, + bytes: &CONTRACT_WASM_BYTES, + signer_account: root + ); + + let alice = root.create_user( + "alice".parse().unwrap(), + to_yocto("100") // initial balance + ); + + (root, contract, alice) +} + +// in first_tests.rs +use near_sdk_sim::{call, view}; +use crate::utils::init; + +#[test] +fn simulate_some_view_function() { + let (root, contract, _alice) = init(); + + let actual: String = view!( + contract.view_something("some_value".to_string()), + ).unwrap_json(); + + assert_eq!("expected", actual); +} + +#[test] +fn simulate_some_change_method() { + let (root, contract, _alice) = init(); + + // uses default gas amount + let result = call!( + root, + contract.change_something("some_value".to_string()), + deposit = 1, + ); + + assert!(result.is_ok()); +} +``` + +# Common patterns + +## Profile gas costs + +For a chain of transactions kicked off by `call` or `call!`, you can check the `gas_burnt` and `tokens_burnt`, where `tokens_burnt` will equal `gas_burnt` multiplied by the `gas_price` set in the genesis config. You can also print out `profile_data` to see an in-depth gas-use breakdown. + +```rust +let outcome = some_account.call( + "some_contract", + "method", + &json({ + "some_param": "some_value", + }).to_string().into_bytes(), + DEFAULT_GAS, + 0, +); + +println!( + "profile_data: {:#?} \n\ntokens_burnt: {}Ⓝ", + outcome.profile_data(), + (outcome.tokens_burnt()) as f64 / 1e24 +); + +let expected_gas_ceiling = 5 * u64::pow(10, 12); // 5 TeraGas +assert!(outcome.gas_burnt() < expected_gas_ceiling); +``` + +TeraGas units are [explained here](https://docs.near.org/docs/concepts/gas#thinking-in-gas). + +Remember to run tests with `--nocapture` to see output from `println!`: + + cargo test -- --nocapture + +The output from this `println!` might look something like this: + + profile_data: ------------------------------ + Total gas: 1891395594588 + Host gas: 1595600369775 [84% total] + Action gas: 0 [0% total] + Wasm execution: 295795224813 [15% total] + ------ Host functions -------- + base -> 7678275219 [0% total, 0% host] + contract_compile_base -> 35445963 [0% total, 0% host] + contract_compile_bytes -> 48341969250 [2% total, 3% host] + read_memory_base -> 28708495200 [1% total, 1% host] + read_memory_byte -> 634822611 [0% total, 0% host] + write_memory_base -> 25234153749 [1% total, 1% host] + write_memory_byte -> 539306856 [0% total, 0% host] + read_register_base -> 20137321488 [1% total, 1% host] + read_register_byte -> 17938284 [0% total, 0% host] + write_register_base -> 25789702374 [1% total, 1% host] + write_register_byte -> 821137824 [0% total, 0% host] + utf8_decoding_base -> 3111779061 [0% total, 0% host] + utf8_decoding_byte -> 15162184908 [0% total, 0% host] + log_base -> 3543313050 [0% total, 0% host] + log_byte -> 686337132 [0% total, 0% host] + storage_write_base -> 192590208000 [10% total, 12% host] + storage_write_key_byte -> 1621105941 [0% total, 0% host] + storage_write_value_byte -> 2047223574 [0% total, 0% host] + storage_write_evicted_byte -> 2119742262 [0% total, 0% host] + storage_read_base -> 169070537250 [8% total, 10% host] + storage_read_key_byte -> 711908259 [0% total, 0% host] + storage_read_value_byte -> 370326330 [0% total, 0% host] + touching_trie_node -> 1046627135190 [55% total, 65% host] + ------ Actions -------- + ------------------------------ + + + tokens_burnt: 0.00043195379520539996Ⓝ + + +## Profile [storage](https://docs.near.org/docs/concepts/storage-staking) costs + +For a `ContractAccount` created with `deploy!` or a `UserAccount` created with `root.create_user`, you can call `account()` to get the [Account](https://github.com/near/nearcore/blob/df2d8bac977461c3abded5ef52ac3000f53e9097/core/primitives-core/src/account.rs#L8-L21) information stored in the simulated blockchain. + +```rs +let account = root.account().unwrap(); +let balance = account.amount; +let locked_in_stake = account.locked; +let storage_usage = account.storage_usage; +``` + +You can use this info to do detailed profiling of how contract calls alter the storage usage of accounts. + + +## Inspect intermediate state of all calls in a complicated chain of transactions + +Say you have a `call` or `call!`: + +```rust +let outcome = some_account.call( + "some_contract", + "method", + &json({ + "some_param": "some_value", + }).to_string().into_bytes(), + DEFAULT_GAS, + 0, +); +``` + +If `some_contract.method` here makes cross-contract calls, `near-sdk-sim` will allow all of these calls to complete. You can then inspect the entire chain of calls via the `outcome` struct. Some useful methods: + +* [`outcome.promise_results()`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L123-L126) +* [`outcome.get_receipt_results()`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L114-L117) +* [`outcome.logs()`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L156-L159) + +You can use these with `println!` and [pretty print interpolation](https://riptutorial.com/rust/example/1248/advanced-usage-of-println-): + +```rust +println!("{:#?}", outcome.promise_results); +``` + +Remember to run your tests with `--nocapture` to see the `println!` output: + + cargo test -- --nocapture + +You might see something like this: + + [ + Some( + ExecutionResult { + outcome: ExecutionOutcome { + logs: [], + receipt_ids: [ + `2bCDBfWgRkzGggXLuiXqhnVGbxwRz7RP3qa8WS5nNw8t`, + ], + burnt_gas: 2428220615156, + tokens_burnt: 0, + status: SuccessReceiptId(2bCDBfWgRkzGggXLuiXqhnVGbxwRz7RP3qa8WS5nNw8t), + }, + }, + ), + Some( + ExecutionResult { + outcome: ExecutionOutcome { + logs: [], + receipt_ids: [], + burnt_gas: 18841799405111, + tokens_burnt: 0, + status: Failure(Action #0: Smart contract panicked: panicked at 'Not an integer: ParseIntError { kind: InvalidDigit }', test-contract/src/lib.rs:85:56), + }, + }, + ) + ] + +You can see it's a little hard to tell which call is which, since the [ExecutionResult](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L20-L27) does not yet include the name of the contract or method. To help debug, you can use `log!` in your contract methods. All `log!` output will show up in the `logs` arrays in the ExecutionOutcomes shown above. + + +## Check expected transaction failures + +If you want to check something in the `logs` or `status` of one of the transactions in one of these call chains mentioned above, you can use string matching. To check that the Failure above matches your expectations, you could: + +```rust +use near_sdk_sim::transaction::ExecutionStatus; + +#[test] +fn simulate_some_failure() { + let outcome = some_account.call(...); + + assert_eq!(res.promise_errors().len(), 1); + + if let ExecutionStatus::Failure(execution_error) = + &outcome.promise_errors().remove(0).unwrap().outcome().status + { + assert!(execution_error.to_string().contains("ParseIntError")); + } else { + unreachable!(); + } +} +``` + +This [`promise_errors`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L128-L135) returns a filtered version of the `promise_results` method mentioned above. + +Parsing `logs` is much simpler, whether [from `get_receipt_results`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/examples/fungible-token/tests/sim/with_macros.rs#L128-L134) or [from `logs` directly](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/examples/fungible-token/tests/sim/with_macros.rs#L70-L74). + + +# Tweaking the genesis config + +For many simulation tests, using `init_simulator(None)` is good enough. This uses the [default genesis configuration settings](https://github.com/near/near-sdk-rs/blob/0a9a56f1590e1f19efc974160c88f32efcb91ef4/near-sdk-sim/src/runtime.rs#L59-L72): + +```rust +GenesisConfig { + genesis_time: 0, + gas_price: 100_000_000, + gas_limit: std::u64::MAX, + genesis_height: 0, + epoch_length: 3, + runtime_config: RuntimeConfig::default(), + state_records: vec![], + validators: vec![], +} +``` + +If you want to override some of these values, for example to simulate how your contract will behave if [gas price](https://docs.near.org/docs/concepts/gas) goes up 10x: + +```rs +use near_sdk_sim::runtime::GenesisConfig; + +pub fn init () { + let mut genesis = GenesisConfig::default(); + genesis.gas_price = genesis.gas_price * 10; + let root = init_simulator(Some(genesis)); +} +``` diff --git a/near-sdk-sim/src/cache.rs b/near-sdk-sim/src/cache.rs new file mode 100644 index 000000000..e0d4b3cca --- /dev/null +++ b/near-sdk-sim/src/cache.rs @@ -0,0 +1,91 @@ +use crate::types::CompiledContractCache; +use std::collections::HashMap; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Write}; +use std::path::{Path, PathBuf}; +use std::sync::{Arc, Mutex}; + +/// This provides a disc cache for compiled contracts. +/// The cached contracts are located `CARGO_MANIFEST_DIR/target/contract_cache`. +#[derive(Clone, Default)] +pub struct ContractCache { + data: Arc, Vec>>>, +} + +pub(crate) fn key_to_b58(key: &[u8]) -> String { + near_sdk::bs58::encode(key).into_string() +} + +impl ContractCache { + pub fn new() -> Self { + ContractCache::default() + } + + fn path() -> PathBuf { + let s = std::env::var("CARGO_MANIFEST_DIR").unwrap(); + Path::new(&s).join("target").join("contract_cache") + } + + fn open_file(&self, key: &[u8]) -> std::io::Result { + let path = self.get_path(key); + // Ensure that the parent path exists + let prefix = path.parent().unwrap(); + std::fs::create_dir_all(prefix).unwrap(); + // Ensure we can read, write, and create file if it doesn't exist + OpenOptions::new().read(true).write(true).create(true).open(path) + } + + fn get_path(&self, key: &[u8]) -> PathBuf { + ContractCache::path().join(key_to_b58(key)) + } + + fn file_exists(&self, key: &[u8]) -> bool { + self.get_path(key).exists() + } + + pub fn insert(&self, key: &[u8], value: &[u8]) -> Option> { + self.data.lock().unwrap().insert(key.to_vec(), value.to_vec()) + } + + pub fn get(&self, key: &[u8]) -> Option> { + self.data.lock().unwrap().get(key).cloned() + } + + #[allow(dead_code)] + pub(crate) fn to_arc(&self) -> Arc { + Arc::new(self.clone()) + } +} + +impl CompiledContractCache for ContractCache { + fn put(&self, key: &[u8], value: &[u8]) -> Result<(), std::io::Error> { + self.insert(key, value); + let mut file = self.open_file(key).expect("File failed to open"); + let metadata = file.metadata()?; + if metadata.len() != value.len() as u64 { + file.write_all(value)?; + } + Ok(()) + } + + fn get(&self, key: &[u8]) -> Result>, std::io::Error> { + if (*self.data).lock().unwrap().contains_key(key) { + return Ok(self.get(key)); + } else if self.file_exists(key) { + let mut file = self.open_file(key)?; + let mut contents = vec![]; + file.read_to_end(&mut contents)?; + self.insert(key, &contents); + return Ok(Some(contents)); + } + Ok(None) + } +} + +pub fn create_cache() -> ContractCache { + ContractCache::new() +} + +pub fn cache_to_arc(cache: &ContractCache) -> Arc { + cache.to_arc() +} diff --git a/near-sdk-sim/src/lib.rs b/near-sdk-sim/src/lib.rs new file mode 100644 index 000000000..eee2f9b08 --- /dev/null +++ b/near-sdk-sim/src/lib.rs @@ -0,0 +1,24 @@ +//! # near_sdk_sim +//! +//! This crate provides an interface for simulating transactions on NEAR's Blockchain. +//! The simulator uses a standalone runtime that can handle any of the [actions](https://nomicon.io/RuntimeSpec/Actions.html) provided by the +//! real runtime, including: creating accounts, deploying contracts, making contract calls and +//! calling view methods. + +pub mod outcome; +#[doc(inline)] +pub use outcome::*; +mod cache; +pub mod runtime; +pub mod units; +pub mod user; +pub use near_crypto; +#[doc(hidden)] +pub use near_primitives::*; +#[doc(inline)] +pub use units::*; +#[doc(inline)] +pub use user::*; + +#[doc(hidden)] +pub use lazy_static_include; diff --git a/near-sdk-sim/src/outcome.rs b/near-sdk-sim/src/outcome.rs new file mode 100644 index 000000000..40f3e2f64 --- /dev/null +++ b/near-sdk-sim/src/outcome.rs @@ -0,0 +1,259 @@ +use crate::hash::CryptoHash; +use crate::runtime::{init_runtime, RuntimeStandalone}; +use crate::transaction::{ExecutionOutcome, ExecutionStatus}; +use core::fmt; +use near_primitives::profile::ProfileData; +use near_primitives::transaction::ExecutionStatus::{SuccessReceiptId, SuccessValue}; +use near_primitives::types::AccountId; +use near_sdk::borsh::BorshDeserialize; +use near_sdk::serde::de::DeserializeOwned; +use near_sdk::serde_json::Value; +use near_sdk::Gas; +use std::borrow::Borrow; +use std::cell::RefCell; +use std::fmt::Debug; +use std::fmt::Formatter; +use std::rc::Rc; + +pub type TxResult = Result; + +/// An ExecutionResult is created by a UserAccount submitting a transaction. +/// It wraps an ExecutionOutcome which is the same object returned from an RPC call. +#[derive(Clone)] +pub struct ExecutionResult { + runtime: Rc>, + outcome: ExecutionOutcome, + hash: CryptoHash, +} + +impl Debug for ExecutionResult { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { + f.debug_struct("ExecutionResult").field("outcome", &self.outcome).finish() + } +} + +impl Default for ExecutionResult { + fn default() -> Self { + ExecutionResult::new( + ExecutionOutcome::default(), + &Rc::new(RefCell::new(init_runtime(None).0)), + CryptoHash::default(), + ) + } +} + +impl ExecutionResult { + #[doc(hidden)] + pub fn new( + outcome: ExecutionOutcome, + runtime: &Rc>, + hash: CryptoHash, + ) -> Self { + Self { runtime: Rc::clone(runtime), outcome, hash } + } + + /// Interpret the SuccessValue as a JSON value + pub fn unwrap_json_value(&self) -> Value { + use crate::transaction::ExecutionStatus::*; + match &(self.outcome).status { + SuccessValue(s) => near_sdk::serde_json::from_slice(s).unwrap(), + err => panic!("Expected Success value but got: {:#?}", err), + } + } + + /// Deserialize SuccessValue from Borsh + pub fn unwrap_borsh(&self) -> T { + use crate::transaction::ExecutionStatus::*; + match &(self.outcome).status { + SuccessValue(s) => BorshDeserialize::try_from_slice(s).unwrap(), + _ => panic!("Cannot get value of failed transaction"), + } + } + + /// Deserialize SuccessValue from JSON + pub fn unwrap_json(&self) -> T { + near_sdk::serde_json::from_value(self.unwrap_json_value()).unwrap() + } + + /// Check if transaction was successful + pub fn is_ok(&self) -> bool { + matches!(&(self.outcome).status, SuccessValue(_) | SuccessReceiptId(_)) + } + + /// Test whether there is a SuccessValue + pub fn has_value(&self) -> bool { + matches!(self.outcome.status, SuccessValue(_)) + } + + /// Asserts that the outcome is successful + pub fn assert_success(&self) { + assert!(self.is_ok(), "Outcome {:#?} was a failure", self.outcome); + } + + /// Lookup an execution result from a hash + pub fn lookup_hash(&self, hash: &CryptoHash) -> Option { + self.get_outcome(hash) + } + + fn get_outcome(&self, hash: &CryptoHash) -> Option { + (*self.runtime) + .borrow() + .outcome(hash) + .map(|out| ExecutionResult::new(out, &self.runtime, *hash)) + } + + /// Reference to internal ExecutionOutcome + pub fn outcome(&self) -> &ExecutionOutcome { + &self.outcome + } + + /// Return results of promises from the `receipt_ids` in the ExecutionOutcome + pub fn get_receipt_results(&self) -> Vec> { + self.get_outcomes(&self.outcome.receipt_ids) + } + + fn get_outcomes(&self, ids: &[CryptoHash]) -> Vec> { + ids.iter().map(|id| self.get_outcome(id)).collect() + } + + /// Return the results of any promises created since the last transaction + pub fn promise_results(&self) -> Vec> { + self.get_outcomes(&(*self.runtime).borrow().last_outcomes) + } + + pub fn promise_errors(&self) -> Vec> { + let mut res = self.promise_results(); + res.retain(|outcome| match outcome { + Some(o) => !o.is_ok(), + _ => false, + }); + res + } + + /// Execution status. Contains the result in case of successful execution. + /// NOTE: Should be the latest field since it contains unparsable by light client + /// ExecutionStatus::Failure + pub fn status(&self) -> ExecutionStatus { + self.outcome.status.clone() + } + + /// The amount of the gas burnt by the given transaction or receipt. + pub fn gas_burnt(&self) -> Gas { + Gas(self.outcome.gas_burnt) + } + + /// The amount of tokens burnt corresponding to the burnt gas amount. + /// This value doesn't always equal to the `gas_burnt` multiplied by the gas price, because + /// the prepaid gas price might be lower than the actual gas price and it creates a deficit. + pub fn tokens_burnt(&self) -> u128 { + self.outcome.tokens_burnt + } + + /// Logs from this transaction or receipt. + pub fn logs(&self) -> &Vec { + &self.outcome.logs + } + + /// The id of the account on which the execution happens. For transaction this is signer_id, + /// for receipt this is receiver_id. + pub fn executor_id(&self) -> &AccountId { + &self.outcome.executor_id + } + + /// Receipt IDs generated by this transaction or receipt. + pub fn receipt_ids(&self) -> &Vec { + &self.outcome.receipt_ids + } + + pub fn profile_data(&self) -> ProfileData { + (*self.runtime).borrow().profile_of_outcome(&self.hash).unwrap() + } +} + +#[doc(hidden)] +pub fn outcome_into_result( + outcome: (CryptoHash, ExecutionOutcome), + runtime: &Rc>, +) -> ExecutionResult { + match (outcome.1).status { + ExecutionStatus::SuccessValue(_) | + ExecutionStatus::Failure(_) => ExecutionResult::new(outcome.1, runtime, outcome.0), + ExecutionStatus::SuccessReceiptId(_) => panic!("Unresolved ExecutionOutcome run runtime.resolve(tx) to resolve the final outcome of tx"), + ExecutionStatus::Unknown => unreachable!() + } +} + +/// The result of a view call. Contains the logs made during the view method call and Result value, +/// which can be unwrapped and deserialized. +#[derive(Debug)] +pub struct ViewResult { + result: Result, Box>, + logs: Vec, +} + +impl ViewResult { + pub fn new(result: Result, Box>, logs: Vec) -> Self { + Self { result, logs } + } + + /// Logs made during the view call + pub fn logs(&self) -> &Vec { + &self.logs + } + + pub fn is_err(&self) -> bool { + self.result.is_err() + } + + pub fn is_ok(&self) -> bool { + self.result.is_ok() + } + + /// Attempt unwrap the value returned by the view call and panic if it is an error + pub fn unwrap(&self) -> Vec { + (&self.result).as_ref().borrow().unwrap().clone() + } + + pub fn unwrap_err(&self) -> &dyn std::error::Error { + (&self.result).as_ref().borrow().unwrap_err().as_ref().borrow() + } + + /// Interpret the value as a JSON::Value + pub fn unwrap_json_value(&self) -> Value { + near_sdk::serde_json::from_slice(self.result.as_ref().expect("ViewResult is an error")) + .unwrap() + } + + /// Deserialize the value with Borsh + pub fn unwrap_borsh(&self) -> T { + BorshDeserialize::try_from_slice(self.result.as_ref().expect("ViewResult is an error")) + .unwrap() + } + + /// Deserialize the value with JSON + pub fn unwrap_json(&self) -> T { + near_sdk::serde_json::from_value(self.unwrap_json_value()).unwrap() + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::runtime::init_runtime; + use near_primitives::transaction::ExecutionStatus::SuccessValue; + use near_sdk::serde_json::json; + + #[test] + fn value_test() { + let value = json!({ + "id": "hello" + }); + let status = SuccessValue(value.to_string().as_bytes().to_vec()); + let outcome = ExecutionOutcome { status, ..Default::default() }; + let result = outcome_into_result( + (CryptoHash::default(), outcome), + &Rc::new(RefCell::new(init_runtime(None).0)), + ); + assert_eq!(value, result.unwrap_json_value()); + } +} diff --git a/near-sdk-sim/src/runtime.rs b/near-sdk-sim/src/runtime.rs new file mode 100644 index 000000000..41bacc7e2 --- /dev/null +++ b/near-sdk-sim/src/runtime.rs @@ -0,0 +1,548 @@ +use std::collections::HashMap; +use std::sync::Arc; + +use crate::cache::{cache_to_arc, create_cache, ContractCache}; +use crate::ViewResult; +use near_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; +use near_pool::{types::PoolIterator, TransactionPool}; +use near_primitives::account::{AccessKey, Account}; +use near_primitives::errors::RuntimeError; +use near_primitives::hash::CryptoHash; +use near_primitives::profile::ProfileData; +use near_primitives::receipt::Receipt; +use near_primitives::runtime::config::RuntimeConfig; +use near_primitives::state_record::StateRecord; +use near_primitives::test_utils::account_new; +use near_primitives::test_utils::MockEpochInfoProvider; +use near_primitives::transaction::{ExecutionOutcome, ExecutionStatus, SignedTransaction}; +use near_primitives::types::{ + AccountInfo, Balance, BlockHeight, EpochHeight, EpochId, EpochInfoProvider, Gas, + StateChangeCause, +}; +use near_primitives::version::PROTOCOL_VERSION; +use near_primitives::views::ViewApplyState; +use near_runtime::{state_viewer::TrieViewer, ApplyState, Runtime}; +use near_sdk::{AccountId, Duration}; +use near_store::{ + get_access_key, get_account, set_account, test_utils::create_test_store, ShardTries, Store, +}; + +const DEFAULT_EPOCH_LENGTH: u64 = 3; +const DEFAULT_BLOCK_PROD_TIME: Duration = 1_000_000_000; + +pub fn init_runtime( + genesis_config: Option, +) -> (RuntimeStandalone, InMemorySigner, AccountId) { + let mut genesis = genesis_config.unwrap_or_default(); + genesis.runtime_config.wasm_config.limit_config.max_total_prepaid_gas = genesis.gas_limit; + let root_account_id: AccountId = AccountId::new_unchecked("root".to_string()); + let signer = genesis.init_root_signer(root_account_id.as_str()); + let runtime = RuntimeStandalone::new_with_store(genesis); + (runtime, signer, root_account_id) +} + +#[derive(Debug)] +pub struct GenesisConfig { + pub genesis_time: u64, + pub gas_price: Balance, + pub gas_limit: Gas, + pub genesis_height: u64, + pub epoch_length: u64, + pub block_prod_time: Duration, + pub runtime_config: RuntimeConfig, + pub state_records: Vec, + pub validators: Vec, +} + +impl Default for GenesisConfig { + fn default() -> Self { + let runtime_config = RuntimeConfig::from_protocol_version( + &Arc::new(RuntimeConfig::default()), + PROTOCOL_VERSION, + ) + .as_ref() + .clone(); + Self { + genesis_time: 0, + gas_price: 100_000_000, + gas_limit: runtime_config.wasm_config.limit_config.max_total_prepaid_gas, + genesis_height: 0, + epoch_length: DEFAULT_EPOCH_LENGTH, + block_prod_time: DEFAULT_BLOCK_PROD_TIME, + runtime_config, + state_records: vec![], + validators: vec![], + } + } +} + +impl GenesisConfig { + pub fn init_root_signer(&mut self, account_id: &str) -> InMemorySigner { + let signer = InMemorySigner::from_seed(account_id, KeyType::ED25519, "test"); + let root_account = account_new(10u128.pow(33), CryptoHash::default()); + + self.state_records.push(StateRecord::Account { + account_id: account_id.to_string(), + account: root_account, + }); + self.state_records.push(StateRecord::AccessKey { + account_id: account_id.to_string(), + public_key: signer.public_key(), + access_key: AccessKey::full_access(), + }); + signer + } +} + +#[derive(Debug, Default, Clone)] +pub struct Block { + prev_block: Option>, + state_root: CryptoHash, + pub epoch_height: EpochHeight, + pub block_height: BlockHeight, + pub block_timestamp: u64, + pub gas_price: Balance, + pub gas_limit: Gas, +} + +impl Drop for Block { + fn drop(&mut self) { + // Blocks form a liked list, so the generated recursive drop overflows + // the stack. Let's use an explicit loop to avoid that. + let mut curr = self.prev_block.take(); + while let Some(mut next) = curr.and_then(|it| Arc::try_unwrap(it).ok()) { + curr = next.prev_block.take(); + } + } +} + +impl Block { + pub fn genesis(genesis_config: &GenesisConfig) -> Self { + Self { + prev_block: None, + state_root: CryptoHash::default(), + block_height: genesis_config.genesis_height, + epoch_height: 0, + block_timestamp: genesis_config.genesis_time, + gas_price: genesis_config.gas_price, + gas_limit: genesis_config.gas_limit, + } + } + + fn produce( + &self, + new_state_root: CryptoHash, + epoch_length: u64, + block_prod_time: Duration, + ) -> Block { + Self { + gas_price: self.gas_price, + gas_limit: self.gas_limit, + block_timestamp: self.block_timestamp + block_prod_time, + prev_block: Some(Arc::new(self.clone())), + state_root: new_state_root, + block_height: self.block_height + 1, + epoch_height: (self.block_height + 1) / epoch_length, + } + } +} + +pub struct RuntimeStandalone { + pub genesis: GenesisConfig, + tx_pool: TransactionPool, + transactions: HashMap, + outcomes: HashMap, + profile: HashMap, + pub cur_block: Block, + runtime: Runtime, + tries: ShardTries, + pending_receipts: Vec, + epoch_info_provider: Box, + pub last_outcomes: Vec, + cache: ContractCache, +} + +impl RuntimeStandalone { + pub fn new(genesis: GenesisConfig, store: Arc) -> Self { + let mut genesis_block = Block::genesis(&genesis); + let mut store_update = store.store_update(); + let runtime = Runtime::new(); + let tries = ShardTries::new(store, 1); + let (s_update, state_root) = runtime.apply_genesis_state( + tries.clone(), + 0, + &[], + &genesis.state_records, + &genesis.runtime_config, + ); + store_update.merge(s_update); + store_update.commit().unwrap(); + genesis_block.state_root = state_root; + let validators = genesis.validators.clone(); + Self { + genesis, + tries, + runtime, + transactions: HashMap::new(), + outcomes: HashMap::new(), + profile: HashMap::new(), + cur_block: genesis_block, + tx_pool: TransactionPool::new(), + pending_receipts: vec![], + epoch_info_provider: Box::new(MockEpochInfoProvider::new( + validators.into_iter().map(|info| (info.account_id, info.amount)), + )), + cache: create_cache(), + last_outcomes: vec![], + } + } + + pub fn new_with_store(genesis: GenesisConfig) -> Self { + RuntimeStandalone::new(genesis, create_test_store()) + } + + /// Processes blocks until the final value is produced + pub fn resolve_tx( + &mut self, + mut tx: SignedTransaction, + ) -> Result<(CryptoHash, ExecutionOutcome), RuntimeError> { + tx.init(); + let mut outcome_hash = tx.get_hash(); + self.transactions.insert(outcome_hash, tx.clone()); + self.tx_pool.insert_transaction(tx); + self.last_outcomes = vec![]; + loop { + self.produce_block()?; + if let Some(outcome) = self.outcomes.get(&outcome_hash) { + match outcome.status { + ExecutionStatus::Unknown => unreachable!(), // ExecutionStatus::Unknown is not relevant for a standalone runtime + ExecutionStatus::SuccessReceiptId(ref id) => outcome_hash = *id, + ExecutionStatus::SuccessValue(_) | ExecutionStatus::Failure(_) => { + return Ok((outcome_hash, outcome.clone())) + } + }; + } else if self.pending_receipts.is_empty() { + unreachable!("Lost an outcome for the receipt hash {}", outcome_hash); + } + } + } + + /// Just puts tx into the transaction pool + pub fn send_tx(&mut self, tx: SignedTransaction) -> CryptoHash { + let tx_hash = tx.get_hash(); + self.transactions.insert(tx_hash, tx.clone()); + self.tx_pool.insert_transaction(tx); + tx_hash + } + + pub fn outcome(&self, hash: &CryptoHash) -> Option { + self.outcomes.get(hash).cloned() + } + + pub fn profile_of_outcome(&self, hash: &CryptoHash) -> Option { + self.profile.get(hash).cloned() + } + + /// Processes all transactions and pending receipts until there is no pending_receipts left + pub fn process_all(&mut self) -> Result<(), RuntimeError> { + loop { + self.produce_block()?; + if self.pending_receipts.is_empty() { + return Ok(()); + } + } + } + + /// Processes one block. Populates outcomes and producining new pending_receipts. + pub fn produce_block(&mut self) -> Result<(), RuntimeError> { + let profile_data = ProfileData::default(); + let apply_state = ApplyState { + block_index: self.cur_block.block_height, + prev_block_hash: Default::default(), + epoch_height: self.cur_block.epoch_height, + gas_price: self.cur_block.gas_price, + block_timestamp: self.cur_block.block_timestamp, + gas_limit: None, + // not used + random_seed: Default::default(), + epoch_id: EpochId::default(), + current_protocol_version: PROTOCOL_VERSION, + config: Arc::new(self.genesis.runtime_config.clone()), + #[cfg(feature = "no_contract_cache")] + cache: None, + #[cfg(not(feature = "no_contract_cache"))] + cache: Some(cache_to_arc(&self.cache)), + profile: profile_data.clone(), + block_hash: Default::default(), + }; + + let apply_result = self.runtime.apply( + self.tries.get_trie_for_shard(0), + self.cur_block.state_root, + &None, + &apply_state, + &self.pending_receipts, + &Self::prepare_transactions(&mut self.tx_pool), + self.epoch_info_provider.as_ref(), + )?; + self.pending_receipts = apply_result.outgoing_receipts; + apply_result.outcomes.iter().for_each(|outcome| { + self.last_outcomes.push(outcome.id); + self.outcomes.insert(outcome.id, outcome.outcome.clone()); + self.profile.insert(outcome.id, profile_data.clone()); + }); + let (update, _) = + self.tries.apply_all(&apply_result.trie_changes, 0).expect("Unexpected Storage error"); + update.commit().expect("Unexpected io error"); + self.cur_block = self.cur_block.produce( + apply_result.state_root, + self.genesis.epoch_length, + self.genesis.block_prod_time, + ); + + Ok(()) + } + + /// Produce num_of_blocks blocks. + /// # Examples + /// + /// ``` + /// use near_sdk_sim::runtime::init_runtime; + /// let (mut runtime, _, _) = init_runtime(None); + /// runtime.produce_blocks(5); + /// assert_eq!(runtime.current_block().block_height, 5); + /// assert_eq!(runtime.current_block().epoch_height, 1); + ///``` + + pub fn produce_blocks(&mut self, num_of_blocks: u64) -> Result<(), RuntimeError> { + for _ in 0..num_of_blocks { + self.produce_block()?; + } + Ok(()) + } + + /// Force alter account and change state_root. + pub fn force_account_update(&mut self, account_id: AccountId, account: &Account) { + let mut trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); + set_account(&mut trie_update, String::from(account_id), account); + trie_update.commit(StateChangeCause::ValidatorAccountsUpdate); + let (trie_changes, _) = trie_update.finalize().expect("Unexpected Storage error"); + let (store_update, new_root) = self.tries.apply_all(&trie_changes, 0).unwrap(); + store_update.commit().expect("No io errors expected"); + self.cur_block.state_root = new_root; + } + + pub fn view_account(&self, account_id: &str) -> Option { + let trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); + get_account(&trie_update, &account_id.to_string()).expect("Unexpected Storage error") + } + + pub fn view_access_key(&self, account_id: &str, public_key: &PublicKey) -> Option { + let trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); + get_access_key(&trie_update, &account_id.to_string(), public_key) + .expect("Unexpected Storage error") + } + + /// Returns a ViewResult containing the value or error and any logs + pub fn view_method_call( + &self, + account_id: &str, + function_name: &str, + args: &[u8], + ) -> ViewResult { + let trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); + let viewer = TrieViewer {}; + let mut logs = vec![]; + let view_state = ViewApplyState { + block_height: self.cur_block.block_height, + prev_block_hash: self.cur_block.prev_block.as_ref().unwrap().state_root, + epoch_id: EpochId::default(), + epoch_height: self.cur_block.epoch_height, + block_timestamp: self.cur_block.block_timestamp, + current_protocol_version: PROTOCOL_VERSION, + cache: Some(cache_to_arc(&self.cache)), + block_hash: self.cur_block.state_root, + }; + let result = viewer.call_function( + trie_update, + view_state, + &account_id.to_string(), + function_name, + args, + &mut logs, + self.epoch_info_provider.as_ref(), + ); + ViewResult::new(result, logs) + } + + /// Returns a reference to the current block. + /// + /// # Examples + /// ``` + /// use near_sdk_sim::runtime::init_runtime; + /// let (mut runtime, _, _) = init_runtime(None); + /// runtime.produce_block().unwrap(); + /// runtime.current_block(); + /// assert_eq!(runtime.current_block().block_height, 1); + /// runtime.produce_blocks(4).unwrap(); + /// assert_eq!(runtime.current_block().block_height, 5); + /// ``` + pub fn current_block(&self) -> &Block { + &self.cur_block + } + + pub fn pending_receipts(&self) -> &[Receipt] { + &self.pending_receipts + } + + fn prepare_transactions(tx_pool: &mut TransactionPool) -> Vec { + let mut res = vec![]; + let mut pool_iter = tx_pool.pool_iterator(); + while let Some(iter) = pool_iter.next() { + if let Some(tx) = iter.next() { + res.push(tx); + } + } + res + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::to_yocto; + + struct Foo {} + + impl Foo { + fn _private(&self) { + print!("yay!") + } + } + + #[test] + fn single_test() { + let _foo = Foo {}; + _foo._private(); + } + + #[test] + fn single_block() { + let (mut runtime, signer, _) = init_runtime(None); + let hash = runtime.send_tx(SignedTransaction::create_account( + 1, + signer.account_id.clone(), + "alice".into(), + 100, + signer.public_key(), + &signer, + CryptoHash::default(), + )); + runtime.produce_block().unwrap(); + assert!(matches!( + runtime.outcome(&hash), + Some(ExecutionOutcome { status: ExecutionStatus::SuccessReceiptId(_), .. }) + )); + } + + #[test] + fn process_all() { + let (mut runtime, signer, _) = init_runtime(None); + assert_eq!(runtime.view_account("alice"), None); + let outcome = runtime.resolve_tx(SignedTransaction::create_account( + 1, + signer.account_id.clone(), + "alice".into(), + 165437999999999999999000, + signer.public_key(), + &signer, + CryptoHash::default(), + )); + assert!(matches!( + outcome, + Ok((_, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })) + )); + assert_eq!( + runtime.view_account("alice"), + Some(Account { + amount: 165437999999999999999000, + code_hash: CryptoHash::default(), + locked: 0, + storage_usage: 182, + }) + ); + } + + #[test] + fn test_cross_contract_call() { + let (mut runtime, signer, _) = init_runtime(None); + + assert!(matches!( + runtime.resolve_tx(SignedTransaction::create_contract( + 1, + signer.account_id.clone(), + "status".into(), + include_bytes!("../../examples/status-message/res/status_message.wasm") + .as_ref() + .into(), + to_yocto("35"), + signer.public_key(), + &signer, + CryptoHash::default(), + )), + Ok((_, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })) + )); + let res = runtime.resolve_tx(SignedTransaction::create_contract( + 2, + signer.account_id.clone(), + "caller".into(), + include_bytes!("../../examples/factory-contract/res/factory_contract_high_level.wasm") + .as_ref() + .into(), + to_yocto("35"), + signer.public_key(), + &signer, + CryptoHash::default(), + )); + assert!(matches!( + res, + Ok((_, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })) + )); + let res = runtime.resolve_tx(SignedTransaction::call( + 3, + signer.account_id.clone(), + "caller".into(), + &signer, + 0, + "simple_call".into(), + "{\"account_id\": \"status\", \"message\": \"caller status is ok!\"}" + .as_bytes() + .to_vec(), + 300_000_000_000_000, + CryptoHash::default(), + )); + let (_, res) = res.unwrap(); + runtime.process_all().unwrap(); + + assert!(matches!(res, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })); + let res = runtime.view_method_call("status", "get_status", b"{\"account_id\": \"root\"}"); + + let caller_status = String::from_utf8(res.unwrap()).unwrap(); + assert_eq!("\"caller status is ok!\"", caller_status); + } + + #[test] + fn test_force_update_account() { + let (mut runtime, _, _) = init_runtime(None); + let mut bob_account = runtime.view_account("root").unwrap(); + bob_account.locked = 10000; + runtime.force_account_update("root".parse().unwrap(), &bob_account); + assert_eq!(runtime.view_account("root").unwrap().locked, 10000); + } + + #[test] + fn can_produce_many_blocks_without_stack_overflow() { + let (mut runtime, _signer, _) = init_runtime(None); + runtime.produce_blocks(20_000).unwrap(); + } +} diff --git a/near-sdk-sim/src/units.rs b/near-sdk-sim/src/units.rs new file mode 100644 index 000000000..d48d3a704 --- /dev/null +++ b/near-sdk-sim/src/units.rs @@ -0,0 +1,20 @@ +pub fn to_nanos(num_days: u64) -> u64 { + num_days * 86_400_000_000_000 +} + +pub fn to_ts(num_days: u64) -> u64 { + // 2018-08-01 UTC in nanoseconds + 1_533_081_600_000_000_000 + to_nanos(num_days) +} + +pub fn to_yocto(value: &str) -> u128 { + let vals: Vec<_> = value.split('.').collect(); + let part1 = vals[0].parse::().unwrap() * 10u128.pow(24); + if vals.len() > 1 { + let power = vals[1].len() as u32; + let part2 = vals[1].parse::().unwrap() * 10u128.pow(24 - power); + part1 + part2 + } else { + part1 + } +} diff --git a/near-sdk-sim/src/user.rs b/near-sdk-sim/src/user.rs new file mode 100644 index 000000000..5fef1f43b --- /dev/null +++ b/near-sdk-sim/src/user.rs @@ -0,0 +1,555 @@ +use std::cell::{Ref, RefCell, RefMut}; +use std::fmt::{Debug, Formatter}; +use std::rc::Rc; + +use near_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; + +use near_sdk::AccountId; +use near_sdk::PendingContractTx; + +use crate::runtime::init_runtime; +pub use crate::to_yocto; +use crate::{ + account::{AccessKey, Account}, + hash::CryptoHash, + outcome_into_result, + runtime::{GenesisConfig, RuntimeStandalone}, + transaction::Transaction, + types::{Balance, Gas}, + ExecutionResult, ViewResult, +}; + +pub const DEFAULT_GAS: u64 = 300_000_000_000_000; +pub const STORAGE_AMOUNT: u128 = 50_000_000_000_000_000_000_000_000; + +type Runtime = Rc>; + +/// A transaction to be signed by the user which created it. Multiple actions can be chained together +/// and then signed and sumited to be executed. +/// +/// # Example: +/// +/// ``` +/// use near_sdk_sim::{to_yocto, account::AccessKey}; +/// use near_crypto::{InMemorySigner, KeyType, Signer}; +/// let master_account = near_sdk_sim::init_simulator(None); +/// let account_id = "alice"; +/// let transaction = master_account.create_transaction(account_id.parse().unwrap()); +/// // Creates a signer which contains a public key. +/// let signer = InMemorySigner::from_seed(account_id, KeyType::ED25519, account_id); +/// let res = transaction.create_account() +/// .add_key(signer.public_key(), AccessKey::full_access()) +/// .transfer(to_yocto("10")) +/// .submit(); +/// ``` +/// +/// This creates an account for `alice`, and a new key pair for the account, adding the +/// public key to the account, and finally transfering `10` NEAR to the account from the +/// `master_account`. +/// +pub struct UserTransaction { + transaction: Transaction, + signer: InMemorySigner, + runtime: Runtime, +} + +impl UserTransaction { + /// Sign and execute the transaction + pub fn submit(self) -> ExecutionResult { + let res = + (*self.runtime).borrow_mut().resolve_tx(self.transaction.sign(&self.signer)).unwrap(); + (*self.runtime).borrow_mut().process_all().unwrap(); + outcome_into_result(res, &self.runtime) + } + + /// Create account for the receiver of the transaction. + pub fn create_account(mut self) -> Self { + self.transaction = self.transaction.create_account(); + self + } + + /// Deploy Wasm binary + pub fn deploy_contract(mut self, code: Vec) -> Self { + self.transaction = self.transaction.deploy_contract(code); + self + } + + /// Execute contract call to receiver + pub fn function_call( + mut self, + function_name: String, + args: Vec, + gas: Gas, + deposit: Balance, + ) -> Self { + self.transaction = self.transaction.function_call(function_name, args, gas, deposit); + self + } + + /// Transfer deposit to receiver + pub fn transfer(mut self, deposit: Balance) -> Self { + self.transaction = self.transaction.transfer(deposit); + self + } + + /// Express interest in becoming a validator + pub fn stake(mut self, stake: Balance, public_key: PublicKey) -> Self { + self.transaction = self.transaction.stake(stake, public_key); + self + } + + /// Add access key, either FunctionCall or FullAccess + pub fn add_key(mut self, public_key: PublicKey, access_key: AccessKey) -> Self { + self.transaction = self.transaction.add_key(public_key, access_key); + self + } + + /// Delete an access key + pub fn delete_key(mut self, public_key: PublicKey) -> Self { + self.transaction = self.transaction.delete_key(public_key); + self + } + + /// Delete an account and send remaining balance to `beneficiary_id` + pub fn delete_account(mut self, beneficiary_id: AccountId) -> Self { + self.transaction = self.transaction.delete_account(String::from(beneficiary_id)); + self + } +} + +/// A user that can sign transactions. It includes a signer and an account id. +pub struct UserAccount { + runtime: Rc>, + pub account_id: AccountId, + pub signer: InMemorySigner, +} + +impl Debug for UserAccount { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("UserAccount").field("account_id", &self.account_id).finish() + } +} + +impl UserAccount { + #[doc(hidden)] + pub fn new( + runtime: &Rc>, + account_id: AccountId, + signer: InMemorySigner, + ) -> Self { + let runtime = Rc::clone(runtime); + Self { runtime, account_id, signer } + } + + /// Returns a copy of the `account_id` + pub fn account_id(&self) -> AccountId { + self.account_id.clone() + } + /// Look up the account information on chain. + pub fn account(&self) -> Option { + (*self.runtime).borrow().view_account(self.account_id.as_str()) + } + /// Transfer yoctoNear to another account + pub fn transfer(&self, to: AccountId, deposit: Balance) -> ExecutionResult { + self.submit_transaction(self.transaction(to).transfer(deposit)) + } + + /// Make a contract call. `pending_tx` includes the receiver, the method to call as well as its arguments. + /// Note: You will most likely not be using this method directly but rather the [`call!`](./macro.call.html) macro. + pub fn function_call( + &self, + pending_tx: PendingContractTx, + gas: Gas, + deposit: Balance, + ) -> ExecutionResult { + self.call(pending_tx.receiver_id, &pending_tx.method, &pending_tx.args, gas, deposit) + } + + pub fn call( + &self, + receiver_id: AccountId, + method: &str, + args: &[u8], + gas: Gas, + deposit: Balance, + ) -> ExecutionResult { + self.submit_transaction(self.transaction(receiver_id).function_call( + method.to_string(), + args.into(), + gas, + deposit, + )) + } + + /// Deploy a contract and create its account for `account_id`. + /// Note: You will most likely not be using this method directly but rather the [`deploy!`](./macro.deploy.html) macro. + pub fn deploy( + &self, + wasm_bytes: &[u8], + account_id: AccountId, + deposit: Balance, + ) -> UserAccount { + let signer = + InMemorySigner::from_seed(account_id.as_str(), KeyType::ED25519, account_id.as_str()); + self.submit_transaction( + self.transaction(account_id.clone()) + .create_account() + .add_key(signer.public_key(), AccessKey::full_access()) + .transfer(deposit) + .deploy_contract(wasm_bytes.to_vec()), + ) + .assert_success(); + UserAccount::new(&self.runtime, account_id, signer) + } + + /// Deploy a contract and in the same transaction call its initialization method. + /// Note: You will most likely not be using this method directly but rather the [`deploy!`](./macro.deploy.html) macro. + pub fn deploy_and_initialize( + &self, + wasm_bytes: &[u8], + pending_tx: PendingContractTx, + deposit: Balance, + gas: Gas, + ) -> UserAccount { + self.deploy_and_init( + wasm_bytes, + pending_tx.receiver_id, + &pending_tx.method, + &pending_tx.args, + deposit, + gas, + ) + } + + pub fn deploy_and_init( + &self, + wasm_bytes: &[u8], + account_id: AccountId, + method: &str, + args: &[u8], + deposit: Balance, + gas: Gas, + ) -> UserAccount { + let signer = + InMemorySigner::from_seed(account_id.as_str(), KeyType::ED25519, account_id.as_str()); + self.submit_transaction( + self.transaction(account_id.clone()) + .create_account() + .add_key(signer.public_key(), AccessKey::full_access()) + .transfer(deposit) + .deploy_contract(wasm_bytes.to_vec()) + .function_call(method.to_string(), args.to_vec(), gas, 0), + ) + .assert_success(); + UserAccount::new(&self.runtime, account_id, signer) + } + + fn transaction(&self, receiver_id: AccountId) -> Transaction { + let nonce = (*self.runtime) + .borrow() + .view_access_key(self.account_id.as_str(), &self.signer.public_key()) + .unwrap() + .nonce + + 1; + Transaction::new( + String::from(self.account_id()), + self.signer.public_key(), + String::from(receiver_id), + nonce, + CryptoHash::default(), + ) + } + + /// Create a user transaction to `receiver_id` to be signed the current user + pub fn create_transaction(&self, receiver_id: AccountId) -> UserTransaction { + let transaction = self.transaction(receiver_id); + let runtime = Rc::clone(&self.runtime); + UserTransaction { transaction, signer: self.signer.clone(), runtime } + } + + fn submit_transaction(&self, transaction: Transaction) -> ExecutionResult { + let res = (*self.runtime).borrow_mut().resolve_tx(transaction.sign(&self.signer)).unwrap(); + (*self.runtime).borrow_mut().process_all().unwrap(); + outcome_into_result(res, &self.runtime) + } + + /// Call a view method on a contract. + /// Note: You will most likely not be using this method directly but rather the [`view!`](./macros.view.html) macro. + pub fn view_method_call(&self, pending_tx: PendingContractTx) -> ViewResult { + self.view(pending_tx.receiver_id, &pending_tx.method, &pending_tx.args) + } + + pub fn view(&self, receiver_id: AccountId, method: &str, args: &[u8]) -> ViewResult { + (*self.runtime).borrow().view_method_call(receiver_id.as_str(), method, args) + } + + /// Creates a user and is signed by the `signer_user` + pub fn create_user_from( + &self, + signer_user: &UserAccount, + account_id: AccountId, + amount: Balance, + ) -> UserAccount { + let signer = + InMemorySigner::from_seed(account_id.as_str(), KeyType::ED25519, account_id.as_str()); + signer_user + .submit_transaction( + signer_user + .transaction(account_id.clone()) + .create_account() + .add_key(signer.public_key(), AccessKey::full_access()) + .transfer(amount), + ) + .assert_success(); + UserAccount::new(&self.runtime, account_id, signer) + } + + /// Create a new user where the signer is this user account + pub fn create_user(&self, account_id: AccountId, amount: Balance) -> UserAccount { + self.create_user_from(self, account_id, amount) + } + + /// Returns a reference to a memory location of the standalone runtime. + /// + /// # Examples + /// ``` + /// let master_account = near_sdk_sim::init_simulator(None); + /// let runtime = master_account.borrow_runtime(); + /// + /// // with use + /// let _block = runtime.current_block(); + /// ``` + pub fn borrow_runtime(&self) -> Ref { + (*self.runtime).borrow() + } + + /// Returns a mutable memory location to the standalone runtime. + /// + /// # Examples + /// ``` + /// let master_account = near_sdk_sim::init_simulator(None); + /// let mut runtime = master_account.borrow_runtime_mut(); + /// + /// // with use + /// runtime.produce_block().unwrap(); + /// ``` + pub fn borrow_runtime_mut(&self) -> RefMut { + (*self.runtime).borrow_mut() + } +} + +/// A account for a contract that includes a reference to the contract proxy and a user account +pub struct ContractAccount { + pub user_account: UserAccount, + pub contract: T, +} + +// TODO: find smarter way to respond to all `self.user_account` methods for a ContractAccount +impl ContractAccount { + pub fn account_id(&self) -> AccountId { + self.user_account.account_id() + } + pub fn account(&self) -> Option { + self.user_account.account() + } +} + +/// The simulator takes an optional GenesisConfig, which sets up the fees and other settings. +/// It returns the `master_account` which can then create accounts and deploy contracts. +pub fn init_simulator(genesis_config: Option) -> UserAccount { + let (runtime, signer, root_account_id) = init_runtime(genesis_config); + UserAccount::new(&Rc::new(RefCell::new(runtime)), root_account_id, signer) +} + +/// Deploys a contract. Will either deploy or deploy and initialize a contract. +/// Returns a `ContractAccount` where `T` is the first argument. +/// +/// # Examples +/// +/// The simplest example is deploying a contract without initializing it. +/// +/// +/// This example deploys and initializes the contract. +/// +/// ``` +/// # lazy_static_include::lazy_static_include_bytes! { +/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", +/// # } +/// use near_sdk_sim::*; +/// use fungible_token::ContractContract; +/// use std::convert::TryInto; +/// use near_sdk::AccountId; +/// let master_account = near_sdk_sim::init_simulator(None); +/// let master_account_id: AccountId = master_account.account_id().try_into().unwrap(); +/// let initial_balance = near_sdk_sim::to_yocto("35"); +/// let contract = deploy! { +/// contract: ContractContract, +/// contract_id: "contract", +/// bytes: &TOKEN_WASM_BYTES, +/// signer_account: master_account, +/// init_method: new_default_meta(master_account_id, initial_balance.into()) +/// }; +/// ``` +/// This example used the default values for the initial deposit to the new contract's account and gas for the contract call. +/// So it is the same as: +/// ``` +/// # lazy_static_include::lazy_static_include_bytes! { +/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", +/// # } +/// use fungible_token::ContractContract; +/// use near_sdk_sim::*; +/// use near_sdk::AccountId; +/// let master_account = near_sdk_sim::init_simulator(None); +/// let master_account_id: AccountId = master_account.account_id(); +/// let initial_balance = near_sdk_sim::to_yocto("35"); +/// let contract = deploy! { +/// contract: ContractContract, +/// contract_id: "contract", +/// bytes: &TOKEN_WASM_BYTES, +/// signer_account: master_account, +/// deposit: near_sdk_sim::STORAGE_AMOUNT, // Deposit required to cover contract storage. +/// gas: near_sdk_sim::DEFAULT_GAS, +/// init_method: new_default_meta(master_account_id, initial_balance.into()), +/// }; +/// ``` +#[macro_export] +macro_rules! deploy { + ($contract: ident, $account_id:expr, $wasm_bytes: expr, $user:expr $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user, near_sdk_sim::STORAGE_AMOUNT) + }; + ($contract: ident, $account_id:expr, $wasm_bytes: expr, $user:expr, $deposit: expr $(,)?) => { + near_sdk_sim::ContractAccount { + user_account: $user.deploy($wasm_bytes, near_sdk::AccountId::new_unchecked($account_id.to_string()), $deposit), + contract: $contract { account_id: near_sdk::AccountId::new_unchecked($account_id.to_string()) }, + } + }; + ($contract: ident, $account_id:expr, $wasm_bytes: expr, $user_id:expr, $deposit:expr, $gas:expr, $method: ident, $($arg:expr),* $(,)?) => { + { + let __contract = $contract { account_id: near_sdk::AccountId::new_unchecked($account_id.to_string()) }; + near_sdk_sim::ContractAccount { + user_account: $user_id.deploy_and_initialize($wasm_bytes, __contract.$method($($arg),*), $deposit, $gas), + contract: __contract, + } + } + }; + (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user) + }; + (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, deposit: $deposit: expr $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user, $deposit) + }; + (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, deposit: $deposit: expr, gas: $gas:expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user, $deposit, $gas, $method, $($arg),*) + }; + (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, gas: $gas:expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user, near_sdk_sim::STORAGE_AMOUNT, $gas, $method, $($arg),*) + }; + (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, deposit: $deposit: expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user, $deposit, near_sdk_sim::DEFAULT_GAS, $method, $($arg),*) + }; + (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { + deploy!($contract, $account_id, $wasm_bytes, $user, near_sdk_sim::STORAGE_AMOUNT, near_sdk_sim::DEFAULT_GAS, $method, $($arg),*) + }; +} + +/// Makes a contract call to a [`ContractAccount`](./struct.ContractAccount.html) returning a [`ExecutionResult`](./struct.ExecutionResult.html). +/// +/// +/// # Examples: +/// +/// ``` +/// # lazy_static_include::lazy_static_include_bytes! { +/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", +/// # } +/// # use near_sdk_sim::*; +/// # use fungible_token::ContractContract; +/// # use std::convert::TryInto; +/// # use near_sdk::AccountId; +/// # let master_account = near_sdk_sim::init_simulator(None); +/// # let master_account_id: AccountId = master_account.account_id().try_into().unwrap(); +/// # let initial_balance = near_sdk_sim::to_yocto("35"); +/// # let contract = deploy! { +/// # contract: ContractContract, +/// # contract_id: "contract", +/// # bytes: &TOKEN_WASM_BYTES, +/// # signer_account: master_account, +/// # deposit: near_sdk_sim::STORAGE_AMOUNT, // Deposit required to cover contract storage. +/// # gas: near_sdk_sim::DEFAULT_GAS, +/// # init_method: new_default_meta(master_account_id.clone(), initial_balance.into()) +/// # }; +/// use near_sdk_sim::to_yocto; +/// // Uses default values for gas and deposit. +/// let res = call!( +/// master_account, +/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None) +/// ); +/// // Equivalent to +/// let res = call!( +/// master_account, +/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None), +/// 0, +/// near_sdk_sim::DEFAULT_GAS +/// ); +/// // Can also specify either deposit or gas +/// let res = call!( +/// master_account, +/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None), +/// deposit = 0 +/// ); +/// let res = call!( +/// master_account, +/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None), +/// gas = near_sdk_sim::DEFAULT_GAS +/// ); +/// ``` +#[macro_export] +macro_rules! call { + ($signer:expr, $deposit: expr, $gas: expr, $contract: ident, $method:ident, $($arg:expr),*) => { + $signer.function_call((&$contract).contract.$method($($arg),*), $gas, $deposit) + }; + ($signer:expr, $contract: ident.$method:ident($($arg:expr),*), $deposit: expr, $gas: expr) => { + call!($signer, $deposit, $gas, $contract, $method, $($arg),*) + }; + ($signer:expr, $contract: ident.$method:ident($($arg:expr),*)) => { + call!($signer, 0, near_sdk_sim::DEFAULT_GAS, $contract, $method, $($arg),*) + }; + ($signer:expr, $contract: ident.$method:ident($($arg:expr),*), gas=$gas_or_deposit: expr) => { + call!($signer, 0, $gas_or_deposit, $contract, $method, $($arg),*) + }; + ($signer:expr, $contract: ident.$method:ident($($arg:expr),*), deposit=$gas_or_deposit: expr) => { + call!($signer, $gas_or_deposit, near_sdk_sim::DEFAULT_GAS, $contract, $method, $($arg),*) + }; +} + +/// Calls a view method on the contract account. +/// +/// Example: +/// ``` +/// # lazy_static_include::lazy_static_include_bytes! { +/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", +/// # } +/// # use near_sdk_sim::*; +/// # use fungible_token::ContractContract; +/// # use std::convert::TryInto; +/// # use near_sdk::AccountId; +/// # let master_account = near_sdk_sim::init_simulator(None); +/// # let master_account_id: AccountId = master_account.account_id().try_into().unwrap(); +/// # let initial_balance = near_sdk_sim::to_yocto("35"); +/// # let contract = deploy! { +/// # contract: ContractContract, +/// # contract_id: "contract", +/// # bytes: &TOKEN_WASM_BYTES, +/// # signer_account: master_account, +/// # deposit: near_sdk_sim::STORAGE_AMOUNT, // Deposit required to cover contract storage. +/// # gas: near_sdk_sim::DEFAULT_GAS, +/// # init_method: new_default_meta(master_account_id.clone(), initial_balance.into()) +/// # }; +/// let res = view!(contract.ft_balance_of(master_account_id)); +/// ``` +/// +#[macro_export] +macro_rules! view { + ($contract: ident.$method:ident($($arg:expr),*)) => { + (&$contract).user_account.view_method_call((&$contract).contract.$method($($arg),*)) + }; +} From ca930d78c3cafd4e68c9ef5aa92cf9850fb8b6b9 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sun, 20 Aug 2023 14:49:47 +0200 Subject: [PATCH 05/35] remove macros, add examples and docs --- Cargo.toml | 2 +- .../src/non_fungible_token/core/core_impl.rs | 4 - .../src/non_fungible_token/macros.rs | 40 ---------- .../src/non_fungible_token/mod.rs | 1 + .../src/non_fungible_token/payout/mod.rs | 76 ++++++++++++++++++- .../non_fungible_token/payout/payout_impl.rs | 11 +-- 6 files changed, 77 insertions(+), 57 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6a7e4184b..5c5a5d5e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ members = [ "near-sdk", "near-sdk-macros", - "near-sdk-sim", +# "near-sdk-sim", "near-contract-standards", "sys", ] diff --git a/near-contract-standards/src/non_fungible_token/core/core_impl.rs b/near-contract-standards/src/non_fungible_token/core/core_impl.rs index 5a9cc8ed4..384db6367 100644 --- a/near-contract-standards/src/non_fungible_token/core/core_impl.rs +++ b/near-contract-standards/src/non_fungible_token/core/core_impl.rs @@ -396,10 +396,6 @@ impl NonFungibleTokenCore for NonFungibleToken { msg: String, ) -> PromiseOrValue { assert_one_yocto(); - require!( - env::prepaid_gas() > GAS_FOR_NFT_TRANSFER_CALL + GAS_FOR_RESOLVE_TRANSFER, - "More gas is required" - ); let sender_id = env::predecessor_account_id(); let (old_owner, old_approvals) = self.internal_transfer(&sender_id, &receiver_id, &token_id, approval_id, memo); diff --git a/near-contract-standards/src/non_fungible_token/macros.rs b/near-contract-standards/src/non_fungible_token/macros.rs index 5cb572394..ecd98045a 100644 --- a/near-contract-standards/src/non_fungible_token/macros.rs +++ b/near-contract-standards/src/non_fungible_token/macros.rs @@ -143,43 +143,3 @@ macro_rules! impl_non_fungible_token_enumeration { } }; } - -/// Non-fungible enumeration adds the extension standard offering several -/// view-only methods to get token supply, tokens per owner, etc. -#[macro_export] -macro_rules! impl_non_fungible_token_payout { - ($contract: ident, $token: ident) => { - use $crate::non_fungible_token::payout::NonFungibleTokenPayout; - - #[near_bindgen] - impl NonFungibleTokenPayout for $contract { - #[allow(unused_variables)] - fn nft_payout( - &self, - token_id: String, - balance: U128, - max_len_payout: Option, - ) -> Payout { - let owner_id = self.tokens.owner_by_id.get(&token_id).expect("No such token_id"); - self.royalties - .get() - .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) - } - #[payable] - fn nft_transfer_payout( - &mut self, - receiver_id: AccountId, - token_id: String, - approval_id: Option, - memo: Option, - balance: U128, - max_len_payout: Option, - ) -> Payout { - assert_one_yocto(); - let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); - self.nft_transfer(receiver_id, token_id, approval_id, memo); - payout - } - } - }; -} diff --git a/near-contract-standards/src/non_fungible_token/mod.rs b/near-contract-standards/src/non_fungible_token/mod.rs index 2cd2eb468..155cc5022 100644 --- a/near-contract-standards/src/non_fungible_token/mod.rs +++ b/near-contract-standards/src/non_fungible_token/mod.rs @@ -29,5 +29,6 @@ pub use self::approval::NonFungibleTokenApproval; pub use self::core::NonFungibleToken; pub use self::core::NonFungibleTokenResolver; pub use self::enumeration::NonFungibleTokenEnumeration; +pub use self::payout::NonFungibleTokenPayout; pub mod events; diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 2bab9faa5..10ef32e22 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -5,20 +5,88 @@ use near_sdk::json_types::U128; use near_sdk::AccountId; use serde::{Deserialize, Serialize}; use std::collections::HashMap; + +type BasisPoint = u16; + #[derive(Default, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] #[serde(crate = "near_sdk::serde")] - pub struct Payout { pub payout: HashMap, } + #[derive(Deserialize, Serialize, BorshDeserialize, BorshSerialize, Default, Debug)] #[serde(crate = "near_sdk::serde")] pub struct Royalties { key_prefix: Vec, - pub accounts: HashMap, - pub percent: u8, + pub accounts: HashMap, + pub percent: BasisPoint, } -/// Offers methods helpful in determining account ownership of NFTs and provides a way to page through NFTs per owner, determine total supply, etc. + +/// An interface allowing non-fungible token contracts to request that financial contracts pay-out +/// multiple receivers, enabling flexible royalty implementations. +/// +/// [Royalties and Payouts standard]: +/// +/// # Examples +/// +/// ``` +/// use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +/// use near_sdk::{PanicOnDefault, AccountId, PromiseOrValue, near_bindgen, assert_one_yocto}; +/// use near_contract_standards::non_fungible_token::{core::NonFungibleTokenCore, NonFungibleToken, NonFungibleTokenPayout, payout::Payout, TokenId, Token}; +/// use near_sdk::json_types::U128; +/// +/// #[near_bindgen] +/// #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] +/// pub struct Contract { +/// tokens: NonFungibleToken, +///} +/// #[near_bindgen] +/// impl NonFungibleTokenCore for Contract { +/// #[payable] +/// fn nft_transfer(&mut self, receiver_id: AccountId, token_id: TokenId, approval_id: Option, memo: Option) { +/// self.tokens.nft_transfer(receiver_id, token_id, approval_id, memo); +/// } +/// +/// #[payable] +/// fn nft_transfer_call(&mut self, receiver_id: AccountId, token_id: TokenId, approval_id: Option, memo: Option, msg: String) -> PromiseOrValue { +/// self.tokens.nft_transfer_call(receiver_id, token_id, approval_id, memo, msg) +/// } +/// +/// fn nft_token(&self, token_id: TokenId) -> Option { +/// self.tokens.nft_token(token_id) +/// } +///} +/// #[near_bindgen] +/// impl NonFungibleTokenPayout for Contract { +/// #[allow(unused_variables)] +/// fn nft_payout( +/// &self, +/// token_id: String, +/// balance: U128, +/// max_len_payout: Option, +/// ) -> Payout { +/// let owner_id = self.tokens.owner_by_id.get(&token_id).expect("No such token_id"); +/// self.tokens.royalties.as_ref() +/// .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) +/// } +/// #[payable] +/// fn nft_transfer_payout( +/// &mut self, +/// receiver_id: AccountId, +/// token_id: String, +/// approval_id: Option, +/// memo: Option, +/// balance: U128, +/// max_len_payout: Option, +/// ) -> Payout { +/// assert_one_yocto(); +/// let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); +/// self.nft_transfer(receiver_id, token_id, approval_id, memo); +/// payout +/// } +/// } +/// ``` +/// pub trait NonFungibleTokenPayout { fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option) -> Payout; /// Given a `token_id` and NEAR-denominated balance, transfer the token diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 17073a0f7..98e4d431c 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -6,16 +6,11 @@ use near_sdk::assert_one_yocto; use near_sdk::{require, AccountId, Balance, IntoStorageKey}; use std::collections::HashMap; impl Royalties { - /* pub fn new(accounts: HashMap, percent: u8) -> Self { - let this = Self { accounts, percent }; - this.validate(); - this - } */ pub fn new(key_prefix: S) -> Self where S: IntoStorageKey, { - let temp_accounts: HashMap = HashMap::new(); + let temp_accounts: HashMap = HashMap::new(); let this = Self { key_prefix: key_prefix.into_storage_key(), accounts: temp_accounts, percent: 0 }; this.validate(); @@ -27,7 +22,7 @@ impl Royalties { self.accounts.len() <= 10, "can only have a maximum of 10 accounts spliting royalties" ); - let mut total: u8 = 0; + let mut total: BasisPoint = 0; self.accounts.iter().for_each(|(_, percent)| { require!(*percent <= 100, "each royalty should be less than 100"); total += percent; @@ -52,7 +47,7 @@ impl Royalties { } } -fn apply_percent(percent: u8, int: u128) -> u128 { +fn apply_percent(percent: BasisPoint, int: u128) -> u128 { int * percent as u128 / 100u128 } From 040603001dcea265423b5360691fc5d00c139be6 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sun, 20 Aug 2023 15:50:14 +0200 Subject: [PATCH 06/35] add royalties storage prefix --- examples/non-fungible-token/nft/src/lib.rs | 68 +++++++++++++++++----- 1 file changed, 55 insertions(+), 13 deletions(-) diff --git a/examples/non-fungible-token/nft/src/lib.rs b/examples/non-fungible-token/nft/src/lib.rs index b89f580b7..c445a5bcb 100644 --- a/examples/non-fungible-token/nft/src/lib.rs +++ b/examples/non-fungible-token/nft/src/lib.rs @@ -15,21 +15,23 @@ NOTES: - To prevent the deployed contract from being modified or deleted, it should not have any access keys on its account. */ -use std::collections::HashMap; +use near_contract_standards::non_fungible_token::approval::NonFungibleTokenApproval; +use near_contract_standards::non_fungible_token::core::{ + NonFungibleTokenCore, NonFungibleTokenResolver, +}; +use near_contract_standards::non_fungible_token::enumeration::NonFungibleTokenEnumeration; use near_contract_standards::non_fungible_token::metadata::{ NFTContractMetadata, NonFungibleTokenMetadataProvider, TokenMetadata, NFT_METADATA_SPEC, }; use near_contract_standards::non_fungible_token::NonFungibleToken; use near_contract_standards::non_fungible_token::{Token, TokenId}; -use near_contract_standards::non_fungible_token::approval::NonFungibleTokenApproval; -use near_contract_standards::non_fungible_token::core::{NonFungibleTokenCore, NonFungibleTokenResolver}; -use near_contract_standards::non_fungible_token::enumeration::NonFungibleTokenEnumeration; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use near_sdk::collections::LazyOption; +use near_sdk::json_types::U128; use near_sdk::{ env, near_bindgen, require, AccountId, BorshStorageKey, PanicOnDefault, Promise, PromiseOrValue, }; -use near_sdk::json_types::U128; +use std::collections::HashMap; #[near_bindgen] #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] @@ -47,6 +49,7 @@ enum StorageKey { TokenMetadata, Enumeration, Approval, + Royalties, } #[near_bindgen] @@ -80,6 +83,7 @@ impl Contract { Some(StorageKey::TokenMetadata), Some(StorageKey::Enumeration), Some(StorageKey::Approval), + Some(StorageKey::Royalties), ), metadata: LazyOption::new(StorageKey::Metadata, Some(&metadata)), } @@ -108,12 +112,25 @@ impl Contract { #[near_bindgen] impl NonFungibleTokenCore for Contract { #[payable] - fn nft_transfer(&mut self, receiver_id: AccountId, token_id: TokenId, approval_id: Option, memo: Option) { + fn nft_transfer( + &mut self, + receiver_id: AccountId, + token_id: TokenId, + approval_id: Option, + memo: Option, + ) { self.tokens.nft_transfer(receiver_id, token_id, approval_id, memo); } #[payable] - fn nft_transfer_call(&mut self, receiver_id: AccountId, token_id: TokenId, approval_id: Option, memo: Option, msg: String) -> PromiseOrValue { + fn nft_transfer_call( + &mut self, + receiver_id: AccountId, + token_id: TokenId, + approval_id: Option, + memo: Option, + msg: String, + ) -> PromiseOrValue { self.tokens.nft_transfer_call(receiver_id, token_id, approval_id, memo, msg) } @@ -125,15 +142,31 @@ impl NonFungibleTokenCore for Contract { #[near_bindgen] impl NonFungibleTokenResolver for Contract { #[private] - fn nft_resolve_transfer(&mut self, previous_owner_id: AccountId, receiver_id: AccountId, token_id: TokenId, approved_account_ids: Option>) -> bool { - self.tokens.nft_resolve_transfer(previous_owner_id, receiver_id, token_id, approved_account_ids) + fn nft_resolve_transfer( + &mut self, + previous_owner_id: AccountId, + receiver_id: AccountId, + token_id: TokenId, + approved_account_ids: Option>, + ) -> bool { + self.tokens.nft_resolve_transfer( + previous_owner_id, + receiver_id, + token_id, + approved_account_ids, + ) } } #[near_bindgen] impl NonFungibleTokenApproval for Contract { #[payable] - fn nft_approve(&mut self, token_id: TokenId, account_id: AccountId, msg: Option) -> Option { + fn nft_approve( + &mut self, + token_id: TokenId, + account_id: AccountId, + msg: Option, + ) -> Option { self.tokens.nft_approve(token_id, account_id, msg) } @@ -145,10 +178,14 @@ impl NonFungibleTokenApproval for Contract { #[payable] fn nft_revoke_all(&mut self, token_id: TokenId) { self.tokens.nft_revoke_all(token_id); - } - fn nft_is_approved(&self, token_id: TokenId, approved_account_id: AccountId, approval_id: Option) -> bool { + fn nft_is_approved( + &self, + token_id: TokenId, + approved_account_id: AccountId, + approval_id: Option, + ) -> bool { self.tokens.nft_is_approved(token_id, approved_account_id, approval_id) } } @@ -167,7 +204,12 @@ impl NonFungibleTokenEnumeration for Contract { self.tokens.nft_supply_for_owner(account_id) } - fn nft_tokens_for_owner(&self, account_id: AccountId, from_index: Option, limit: Option) -> Vec { + fn nft_tokens_for_owner( + &self, + account_id: AccountId, + from_index: Option, + limit: Option, + ) -> Vec { self.tokens.nft_tokens_for_owner(account_id, from_index, limit) } } From c8bd1376a64e1bf27fdb898d6e33b80c11d41c66 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sun, 20 Aug 2023 21:15:20 +0200 Subject: [PATCH 07/35] examples --- examples/non-fungible-token/nft/src/lib.rs | 31 ++++++- .../tests/workspaces/test_core.rs | 80 +++++-------------- 2 files changed, 49 insertions(+), 62 deletions(-) diff --git a/examples/non-fungible-token/nft/src/lib.rs b/examples/non-fungible-token/nft/src/lib.rs index c445a5bcb..bc319f674 100644 --- a/examples/non-fungible-token/nft/src/lib.rs +++ b/examples/non-fungible-token/nft/src/lib.rs @@ -23,13 +23,15 @@ use near_contract_standards::non_fungible_token::enumeration::NonFungibleTokenEn use near_contract_standards::non_fungible_token::metadata::{ NFTContractMetadata, NonFungibleTokenMetadataProvider, TokenMetadata, NFT_METADATA_SPEC, }; -use near_contract_standards::non_fungible_token::NonFungibleToken; +use near_contract_standards::non_fungible_token::payout::Payout; +use near_contract_standards::non_fungible_token::{NonFungibleToken, NonFungibleTokenPayout}; use near_contract_standards::non_fungible_token::{Token, TokenId}; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use near_sdk::collections::LazyOption; use near_sdk::json_types::U128; use near_sdk::{ - env, near_bindgen, require, AccountId, BorshStorageKey, PanicOnDefault, Promise, PromiseOrValue, + assert_one_yocto, env, near_bindgen, require, AccountId, BorshStorageKey, PanicOnDefault, + Promise, PromiseOrValue, }; use std::collections::HashMap; @@ -214,6 +216,31 @@ impl NonFungibleTokenEnumeration for Contract { } } +impl NonFungibleTokenPayout for Contract { + fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option) -> Payout { + let owner_id = self.tokens.owner_by_id.get(&token_id).expect("No such token_id"); + self.tokens + .royalties + .as_ref() + .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) + } + + fn nft_transfer_payout( + &mut self, + receiver_id: AccountId, + token_id: String, + approval_id: Option, + memo: Option, + balance: U128, + max_len_payout: Option, + ) -> Payout { + assert_one_yocto(); + let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); + self.nft_transfer(receiver_id, token_id, approval_id, memo); + payout + } +} + #[near_bindgen] impl NonFungibleTokenMetadataProvider for Contract { fn nft_metadata(&self) -> NFTContractMetadata { diff --git a/examples/non-fungible-token/tests/workspaces/test_core.rs b/examples/non-fungible-token/tests/workspaces/test_core.rs index 017874710..a7fafa85b 100644 --- a/examples/non-fungible-token/tests/workspaces/test_core.rs +++ b/examples/non-fungible-token/tests/workspaces/test_core.rs @@ -8,22 +8,13 @@ async fn simulate_simple_transfer() -> anyhow::Result<()> { let worker = workspaces::sandbox().await?; let (nft_contract, alice, _, _) = init(&worker).await?; - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); let res = nft_contract .call("nft_transfer") - .args_json(( - alice.id(), - TOKEN_ID, - Option::::None, - Some("simple transfer".to_string()), - )) + .args_json((alice.id(), TOKEN_ID, Option::::None, Some("simple transfer".to_string()))) .max_gas() .deposit(ONE_YOCTO) .transact() @@ -33,12 +24,8 @@ async fn simulate_simple_transfer() -> anyhow::Result<()> { // A single NFT transfer event should have been logged: assert_eq!(res.logs().len(), 1); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), alice.id().to_string()); Ok(()) @@ -64,12 +51,8 @@ async fn simulate_transfer_call_fast_return_to_sender() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) @@ -95,12 +78,8 @@ async fn simulate_transfer_call_slow_return_to_sender() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) @@ -127,12 +106,8 @@ async fn simulate_transfer_call_fast_keep_with_sender() -> anyhow::Result<()> { assert!(res.is_success()); assert_eq!(res.logs().len(), 2); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), token_receiver_contract.id().to_string()); Ok(()) @@ -158,12 +133,8 @@ async fn simulate_transfer_call_slow_keep_with_sender() -> anyhow::Result<()> { .await?; assert!(res.is_success()); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), token_receiver_contract.id().to_string()); Ok(()) @@ -192,12 +163,8 @@ async fn simulate_transfer_call_receiver_panics() -> anyhow::Result<()> { // Prints final logs assert_eq!(res.logs().len(), 3); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) @@ -222,17 +189,14 @@ async fn simulate_transfer_call_receiver_panics_and_nft_resolve_transfer_produce .deposit(ONE_YOCTO) .transact() .await?; + assert!(res.is_failure()); // Prints no logs assert_eq!(res.logs().len(), 0); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) @@ -256,12 +220,8 @@ async fn simulate_simple_transfer_no_logs_on_failure() -> anyhow::Result<()> { // Prints no logs assert_eq!(res.logs().len(), 0); - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); Ok(()) From 8e5e77787038acf5ffefc132bd0fe463f3db6a1b Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sun, 10 Sep 2023 17:50:10 +0200 Subject: [PATCH 08/35] remove near-sdk-sim --- Cargo.toml | 1 - near-sdk-sim/Cargo.lock | 1987 ----------------------------------- near-sdk-sim/Cargo.toml | 38 - near-sdk-sim/README.md | 472 --------- near-sdk-sim/src/cache.rs | 91 -- near-sdk-sim/src/lib.rs | 24 - near-sdk-sim/src/outcome.rs | 259 ----- near-sdk-sim/src/runtime.rs | 548 ---------- near-sdk-sim/src/units.rs | 20 - near-sdk-sim/src/user.rs | 555 ---------- 10 files changed, 3995 deletions(-) delete mode 100644 near-sdk-sim/Cargo.lock delete mode 100644 near-sdk-sim/Cargo.toml delete mode 100644 near-sdk-sim/README.md delete mode 100644 near-sdk-sim/src/cache.rs delete mode 100644 near-sdk-sim/src/lib.rs delete mode 100644 near-sdk-sim/src/outcome.rs delete mode 100644 near-sdk-sim/src/runtime.rs delete mode 100644 near-sdk-sim/src/units.rs delete mode 100644 near-sdk-sim/src/user.rs diff --git a/Cargo.toml b/Cargo.toml index 96f9dee2d..86a74b66e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "near-sdk", "near-sdk-macros", -# "near-sdk-sim", "near-contract-standards", "near-sys", ] diff --git a/near-sdk-sim/Cargo.lock b/near-sdk-sim/Cargo.lock deleted file mode 100644 index 50ec6a1c7..000000000 --- a/near-sdk-sim/Cargo.lock +++ /dev/null @@ -1,1987 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -[[package]] -name = "Inflector" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe438c63458706e03479442743baae6c88256498e6431708f6dfc520a26515d3" - -[[package]] -name = "aho-corasick" -version = "0.7.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "043164d8ba5c4c3035fec9bbee8647c0261d788f3474306f93bb65901cae0e86" -dependencies = [ - "memchr", -] - -[[package]] -name = "ansi_term" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b" -dependencies = [ - "winapi", -] - -[[package]] -name = "arrayref" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4c527152e37cf757a3f78aae5a06fbeefdb07ccc535c980a3208ee3060dd544" - -[[package]] -name = "arrayvec" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cff77d8686867eceff3105329d4698d96c2391c176d5d03adc90c7389162b5b8" - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi", - "libc", - "winapi", -] - -[[package]] -name = "autocfg" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" - -[[package]] -name = "base64" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b41b7ea54a0c9d92199de89e20e58d49f02f8e699814ef3fdf266f6f748d15c7" - -[[package]] -name = "bincode" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30d3a39baa26f9651f17b375061f3233dde33424a8b72b0dbe93a68a0bc896d" -dependencies = [ - "byteorder", - "serde", -] - -[[package]] -name = "bindgen" -version = "0.54.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66c0bb6167449588ff70803f4127f0684f9063097eca5016f37eb52b92c2cf36" -dependencies = [ - "bitflags", - "cexpr", - "cfg-if", - "clang-sys", - "clap", - "env_logger", - "lazy_static", - "lazycell", - "log", - "peeking_take_while", - "proc-macro2", - "quote", - "regex", - "rustc-hash", - "shlex", - "which", -] - -[[package]] -name = "bitflags" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" - -[[package]] -name = "blake2" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94cb07b0da6a73955f8fb85d24c466778e70cda767a568229b104f0264089330" -dependencies = [ - "byte-tools", - "crypto-mac 0.7.0", - "digest 0.8.1", - "opaque-debug", -] - -[[package]] -name = "blake3" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce4f9586c9a3151c4b49b19e82ba163dd073614dd057e53c969e1a4db5b52720" -dependencies = [ - "arrayref", - "arrayvec", - "cc", - "cfg-if", - "constant_time_eq", - "crypto-mac 0.8.0", - "digest 0.9.0", -] - -[[package]] -name = "block-buffer" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0940dc441f31689269e10ac70eb1002a3a1d3ad1390e030043662eb7fe4688b" -dependencies = [ - "block-padding", - "byte-tools", - "byteorder", - "generic-array 0.12.3", -] - -[[package]] -name = "block-padding" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa79dedbb091f449f1f39e53edf88d5dbe95f895dae6135a8d7b881fb5af73f5" -dependencies = [ - "byte-tools", -] - -[[package]] -name = "borsh" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7769f8f6fdc6ac7617bbc8bc7ef9dc263cd459d99d21cf2ab4afc3bc8d7d70d" -dependencies = [ - "borsh-derive 0.6.2", -] - -[[package]] -name = "borsh" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d42092adf8d207d987cb8d676f068305a80b3dd58487a06680e8586e7c4c25" -dependencies = [ - "borsh-derive 0.7.0", -] - -[[package]] -name = "borsh-derive" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2689a82a5fe57f9e71997b16bea340da338c7fb8db400b8d9d55b59010540d8" -dependencies = [ - "borsh-derive-internal 0.6.2", - "borsh-schema-derive-internal 0.6.2", - "syn", -] - -[[package]] -name = "borsh-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "912b5e5f545801db1290ea4f05c8af512db0805dade9b9af76798c5fc4a04164" -dependencies = [ - "borsh-derive-internal 0.7.0", - "borsh-schema-derive-internal 0.7.0", - "syn", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39b621f19e9891a34f679034fa2238260e27c0eddfe2804e9fb282061cf9b294" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "borsh-derive-internal" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e75ae71ba613fe0b86556e910b053b040cb52b8c52c034370744f8760f176262" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "befebdb9e223ae4528b3d597dbbfb5c68566822d2a3de3e260f235360773ba29" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "borsh-schema-derive-internal" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1de62f1b5fe76adc6510a4e7c3feb0172650f0088bbd5fead41c0c016db5c5ae" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "bs58" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "476e9cd489f9e121e02ffa6014a8ef220ecb15c05ed23fc34cca13925dc283fb" - -[[package]] -name = "byte-tools" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" - -[[package]] -name = "byteorder" -version = "1.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08c48aae112d48ed9f069b33538ea9e3e90aa263cfa3d1c24309612b1f7472de" - -[[package]] -name = "c2-chacha" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "217192c943108d8b13bac38a1d51df9ce8a407a3f5a71ab633980665e68fbd9a" -dependencies = [ - "byteorder", - "ppv-lite86", - "stream-cipher", -] - -[[package]] -name = "cached" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "083dc50149e37dfaab1ffe2c3af03576b79550f1e419a5091b28a7191db1c45b" -dependencies = [ - "once_cell", -] - -[[package]] -name = "cc" -version = "1.0.59" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66120af515773fb005778dc07c261bd201ec8ce50bd6e7144c927753fe013381" -dependencies = [ - "jobserver", -] - -[[package]] -name = "cexpr" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" -dependencies = [ - "nom", -] - -[[package]] -name = "cfg-if" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" - -[[package]] -name = "chrono" -version = "0.4.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942f72db697d8767c22d46a598e01f2d3b475501ea43d0db4f16d90259182d0b" -dependencies = [ - "num-integer", - "num-traits", - "serde", - "time", -] - -[[package]] -name = "clang-sys" -version = "0.29.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6837df1d5cba2397b835c8530f51723267e16abbf83892e9e5af4f0e5dd10a" -dependencies = [ - "glob", - "libc", - "libloading", -] - -[[package]] -name = "clap" -version = "2.33.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002" -dependencies = [ - "ansi_term", - "atty", - "bitflags", - "strsim", - "textwrap", - "unicode-width", - "vec_map", -] - -[[package]] -name = "clear_on_drop" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c9cc5db465b294c3fa986d5bbb0f3017cd850bff6dd6c52f9ccff8b4d21b7b08" -dependencies = [ - "cc", -] - -[[package]] -name = "cloudabi" -version = "0.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc5b9aa5d4507acaf872de71051dfd0e309860e88966e1051e462a077aac4f" -dependencies = [ - "bitflags", -] - -[[package]] -name = "constant_time_eq" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" - -[[package]] -name = "crunchy" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" - -[[package]] -name = "crypto-mac" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5" -dependencies = [ - "generic-array 0.12.3", - "subtle 1.0.0", -] - -[[package]] -name = "crypto-mac" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b584a330336237c1eecd3e94266efb216c56ed91225d634cb2991c5f3fd1aeab" -dependencies = [ - "generic-array 0.14.4", - "subtle 2.2.3", -] - -[[package]] -name = "curve25519-dalek" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d85653f070353a16313d0046f173f70d1aadd5b42600a14de626f0dfb3473a5" -dependencies = [ - "byteorder", - "digest 0.8.1", - "rand_core", - "subtle 2.2.3", - "zeroize", -] - -[[package]] -name = "derive_more" -version = "0.99.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "298998b1cf6b5b2c8a7b023dfd45821825ce3ba8a8af55c921a0e734e4653f76" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "digest" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3d0c8c8752312f9713efd397ff63acb9f85585afbf179282e720e7704954dd5" -dependencies = [ - "generic-array 0.12.3", -] - -[[package]] -name = "digest" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066" -dependencies = [ - "generic-array 0.14.4", -] - -[[package]] -name = "dynasm" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42a814e1edeb85dd2a3c6fc0d6bf76d02ca5695d438c70ecee3d90774f3259c5" -dependencies = [ - "bitflags", - "byteorder", - "lazy_static", - "owning_ref", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dynasmrt" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a393aaeb4441a48bcf47b5b6155971f82cc1eb77e22855403ccc0415ac8328d" -dependencies = [ - "byteorder", - "memmap", -] - -[[package]] -name = "easy-ext" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73bb6373ab18cda357d060e1cc36ca2f3fc2783a81b033087a55ac2829cfa737" -dependencies = [ - "quote", - "syn", -] - -[[package]] -name = "ed25519-dalek" -version = "1.0.0-pre.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978710b352437433c97b2bff193f2fb1dfd58a093f863dd95e225a19baa599a2" -dependencies = [ - "clear_on_drop", - "curve25519-dalek", - "rand", - "sha2", -] - -[[package]] -name = "elastic-array" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d63720ea2bc2e1b79f7aa044d9dc0b825f9ccb6930b32120f8fb9e873aa84bc" -dependencies = [ - "heapsize", -] - -[[package]] -name = "env_logger" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "errno" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6eab5ee3df98a279d9b316b1af6ac95422127b1290317e6d18c1743c99418b01" -dependencies = [ - "errno-dragonfly", - "libc", - "winapi", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14ca354e36190500e1e1fb267c647932382b54053c50b14970856c0b00a35067" -dependencies = [ - "gcc", - "libc", -] - -[[package]] -name = "fake-simd" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed" - -[[package]] -name = "fixed-hash" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11498d382790b7a8f2fd211780bec78619bba81cdad3a283997c0c41f836759c" -dependencies = [ - "static_assertions", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "fs_extra" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f2a4a2034423744d2cc7ca2068453168dcdb82c438419e639a26bd87839c674" - -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - -[[package]] -name = "generic-array" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c68f0274ae0e023facc3c97b2e00f076be70e254bc851d972503b328db79b2ec" -dependencies = [ - "typenum", -] - -[[package]] -name = "generic-array" -version = "0.14.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "getrandom" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7abc8dd8451921606d809ba32e95b6111925cd2906060d2dcc29c070220503eb" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "glob" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574" - -[[package]] -name = "hashbrown" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e91b62f79061a0bc2e046024cb7ba44b08419ed238ecbd9adbd787434b9e8c25" -dependencies = [ - "autocfg", -] - -[[package]] -name = "heapsize" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1679e6ea370dee694f91f1dc469bf94cf8f52051d147aec3e1f9497c6fc22461" -dependencies = [ - "winapi", -] - -[[package]] -name = "heck" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" -dependencies = [ - "unicode-segmentation", -] - -[[package]] -name = "hermit-abi" -version = "0.1.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3deed196b6e7f9e44a2ae8d94225d80302d81208b1bb673fd21fe634645c85a9" -dependencies = [ - "libc", -] - -[[package]] -name = "hex" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "644f9158b2f133fd50f5fb3242878846d9eb792e445c893805ff0e3824006e35" - -[[package]] -name = "humantime" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df004cfca50ef23c36850aaaa59ad52cc70d0e90243c3c7737a4dd32dc7a3c4f" -dependencies = [ - "quick-error", -] - -[[package]] -name = "idna" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02e2673c30ee86b5b96a9cb52ad15718aa1f966f5ab9ad54a8b95d5ca33120a9" -dependencies = [ - "matches", - "unicode-bidi", - "unicode-normalization", -] - -[[package]] -name = "if_chain" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3360c7b59e5ffa2653671fb74b4741a5d343c03f331c0a4aeda42b5c2b0ec7d" - -[[package]] -name = "indexmap" -version = "1.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b45e59b16c76b11bf9738fd5d38879d3bd28ad292d7b313608becb17ae2df9" -dependencies = [ - "autocfg", - "hashbrown", - "serde", -] - -[[package]] -name = "itoa" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc6f3ad7b9d11a0c00842ff8de1b60ee58661048eb8049ed33c73594f359d7e6" - -[[package]] -name = "jemalloc-sys" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d3b9f3f5c9b31aa0f5ed3260385ac205db665baa41d49bb8338008ae94ede45" -dependencies = [ - "cc", - "fs_extra", - "libc", -] - -[[package]] -name = "jemallocator" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43ae63fcfc45e99ab3d1b29a46782ad679e98436c3169d15a167a1108a724b69" -dependencies = [ - "jemalloc-sys", - "libc", -] - -[[package]] -name = "jobserver" -version = "0.1.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c71313ebb9439f74b00d9d2dcec36440beaf57a6aa0623068441dd7cd81a7f2" -dependencies = [ - "libc", -] - -[[package]] -name = "keccak" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "lazycell" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" - -[[package]] -name = "libc" -version = "0.2.76" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "755456fae044e6fa1ebbbd1b3e902ae19e73097ed4ed87bb79934a867c007bc3" - -[[package]] -name = "libloading" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2b111a074963af1d37a139918ac6d49ad1d0d5e47f72fd55388619691a7d753" -dependencies = [ - "cc", - "winapi", -] - -[[package]] -name = "librocksdb-sys" -version = "6.10.2" -source = "git+https://github.com/nearprotocol/rust-rocksdb?branch=disable-thread#44592812fd12cc2ea5de55af7c8d9bfdeacac692" -dependencies = [ - "bindgen", - "cc", - "glob", - "libc", -] - -[[package]] -name = "lock_api" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4da24a77a3d8a6d4862d95f72e6fdb9c09a643ecdb402d754004a557f2bec75" -dependencies = [ - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fabed175da42fed1fa0746b0ea71f412aa9d35e76e95e59b192c64b9dc2bf8b" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "matches" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" - -[[package]] -name = "memchr" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3728d817d99e5ac407411fa471ff9800a778d88a24685968b36824eaf4bee400" - -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "near-crypto" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "arrayref", - "blake2", - "borsh 0.7.0", - "bs58", - "c2-chacha", - "curve25519-dalek", - "digest 0.8.1", - "ed25519-dalek", - "lazy_static", - "libc", - "parity-secp256k1", - "rand", - "rand_core", - "serde", - "serde_json", - "sha2", - "subtle 2.2.3", -] - -[[package]] -name = "near-metrics" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "lazy_static", - "log", - "prometheus", -] - -[[package]] -name = "near-pool" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "borsh 0.7.0", - "near-crypto", - "near-primitives", - "rand", -] - -[[package]] -name = "near-primitives" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "base64", - "borsh 0.7.0", - "bs58", - "byteorder", - "chrono", - "derive_more", - "easy-ext", - "hex", - "jemallocator", - "lazy_static", - "near-crypto", - "near-rpc-error-macro 0.1.0 (git+https://github.com/nearprotocol/nearcore.git)", - "near-vm-errors 1.2.0", - "num-rational", - "primitive-types", - "rand", - "reed-solomon-erasure", - "regex", - "serde", - "serde_json", - "sha2", - "smart-default", - "validator", - "validator_derive", -] - -[[package]] -name = "near-rpc-error-core" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn", -] - -[[package]] -name = "near-rpc-error-core" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffa8dbf8437a28ac40fcb85859ab0d0b8385013935b000c7a51ae79631dd74d9" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn", -] - -[[package]] -name = "near-rpc-error-macro" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "near-rpc-error-core 0.1.0 (git+https://github.com/nearprotocol/nearcore.git)", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn", -] - -[[package]] -name = "near-rpc-error-macro" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c6111d713e90c7c551dee937f4a06cb9ea2672243455a4454cc7566387ba2d9" -dependencies = [ - "near-rpc-error-core 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2", - "quote", - "serde", - "serde_json", - "syn", -] - -[[package]] -name = "near-runtime-configs" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "near-primitives", - "near-runtime-fees 1.2.0", - "near-vm-logic 1.2.0", - "serde", -] - -[[package]] -name = "near-runtime-fees" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4992274c8acb33fa1246715d3aafbce5688ae82243c779b561f8eaff1bb6f1" -dependencies = [ - "num-rational", - "serde", -] - -[[package]] -name = "near-runtime-fees" -version = "1.2.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "num-rational", - "serde", -] - -[[package]] -name = "near-runtime-standalone" -version = "1.2.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "near-crypto", - "near-pool", - "near-primitives", - "near-runtime-configs", - "near-store", - "node-runtime", -] - -[[package]] -name = "near-sdk" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81319d4d44283f63467e4f02b6209297b10643c7aeb62e2ee41e0c31b43e2375" -dependencies = [ - "base64", - "borsh 0.6.2", - "bs58", - "near-runtime-fees 0.9.1", - "near-sdk-macros", - "near-vm-logic 0.9.1", - "serde", -] - -[[package]] -name = "near-sdk-core" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3767fc2a61e6577f1336e06d6962a6c61fc39299573b8a25696fd09ce96ffb" -dependencies = [ - "Inflector", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "near-sdk-macros" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27c06b45c56028b0e1241b2196397d449091665f3f08d543415373505df5e05f" -dependencies = [ - "near-sdk-core", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "near-store" -version = "0.1.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "borsh 0.7.0", - "byteorder", - "cached", - "derive_more", - "elastic-array", - "lazy_static", - "near-crypto", - "near-primitives", - "num_cpus", - "rand", - "rocksdb", - "serde", - "serde_json", - "strum", - "strum_macros", -] - -[[package]] -name = "near-test" -version = "0.1.0" -dependencies = [ - "borsh 0.6.2", - "env_logger", - "log", - "near-crypto", - "near-primitives", - "near-runtime-standalone", - "near-sdk", - "quickcheck", - "quickcheck_macros", - "serde", - "serde_json", -] - -[[package]] -name = "near-vm-errors" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "386c2c07ef37ae52ad43860ef69c6322bbc1e610ae0c08c1d7f5ff56f4c28e6a" -dependencies = [ - "borsh 0.6.2", - "near-rpc-error-macro 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde", -] - -[[package]] -name = "near-vm-errors" -version = "1.2.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "borsh 0.7.0", - "near-rpc-error-macro 0.1.0 (git+https://github.com/nearprotocol/nearcore.git)", - "serde", -] - -[[package]] -name = "near-vm-logic" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6da6c80d3428f45248577820bfc943b8261a6f11d6721037e5c3f43484047cd" -dependencies = [ - "base64", - "bs58", - "byteorder", - "near-runtime-fees 0.9.1", - "near-vm-errors 0.9.1", - "serde", - "sha2", - "sha3", -] - -[[package]] -name = "near-vm-logic" -version = "1.2.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "base64", - "bs58", - "byteorder", - "near-runtime-fees 1.2.0", - "near-vm-errors 1.2.0", - "serde", - "sha2", - "sha3", -] - -[[package]] -name = "near-vm-runner" -version = "1.2.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "cached", - "near-runtime-fees 1.2.0", - "near-vm-errors 1.2.0", - "near-vm-logic 1.2.0", - "parity-wasm", - "pwasm-utils", - "wasmer-runtime", - "wasmer-runtime-core", -] - -[[package]] -name = "nix" -version = "0.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b2e0b4f3320ed72aaedb9a5ac838690a8047c7b275da22711fddff4f8a14229" -dependencies = [ - "bitflags", - "cc", - "cfg-if", - "libc", - "void", -] - -[[package]] -name = "node-runtime" -version = "1.2.0" -source = "git+https://github.com/nearprotocol/nearcore.git#6eb8ab56f5754b8b16f907c068cfed69a637fc21" -dependencies = [ - "borsh 0.7.0", - "byteorder", - "cached", - "lazy_static", - "log", - "near-crypto", - "near-metrics", - "near-primitives", - "near-runtime-configs", - "near-runtime-fees 1.2.0", - "near-store", - "near-vm-errors 1.2.0", - "near-vm-logic 1.2.0", - "near-vm-runner", - "num-bigint", - "num-rational", - "num-traits", - "rand", - "rocksdb", - "serde", - "serde_json", -] - -[[package]] -name = "nom" -version = "5.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af" -dependencies = [ - "memchr", - "version_check", -] - -[[package]] -name = "num-bigint" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-integer" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d59457e662d541ba17869cf51cf177c0b5f0cbf476c66bdc90bf1edac4f875b" -dependencies = [ - "autocfg", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c000134b5dbf44adc5cb772486d335293351644b801551abe8f75c84cfa4aef" -dependencies = [ - "autocfg", - "num-bigint", - "num-integer", - "num-traits", - "serde", -] - -[[package]] -name = "num-traits" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac267bcc07f48ee5f8935ab0d24f316fb722d7a1292e2913f0cc196b29ffd611" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "once_cell" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "260e51e7efe62b592207e9e13a68e43692a7a279171d6ba57abd208bf23645ad" - -[[package]] -name = "opaque-debug" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2839e79665f131bdb5782e51f2c6c9599c133c6098982a54c794358bf432529c" - -[[package]] -name = "owning_ref" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ff55baddef9e4ad00f88b6c743a2a8062d4c6ade126c2a528644b8e444d52ce" -dependencies = [ - "stable_deref_trait", -] - -[[package]] -name = "page_size" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebde548fbbf1ea81a99b128872779c437752fb99f217c45245e1a61dcd9edcd" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "parity-secp256k1" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fca4f82fccae37e8bbdaeb949a4a218a1bbc485d11598f193d2a908042e5fc1" -dependencies = [ - "arrayvec", - "cc", - "cfg-if", - "rand", -] - -[[package]] -name = "parity-wasm" -version = "0.41.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddfc878dac00da22f8f61e7af3157988424567ab01d9920b962ef7dcbd7cd865" - -[[package]] -name = "parking_lot" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3a704eb390aafdc107b0e392f56a82b668e3a71366993b5340f5833fd62505e" -dependencies = [ - "lock_api", - "parking_lot_core", -] - -[[package]] -name = "parking_lot_core" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d58c7c768d4ba344e3e8d72518ac13e259d7c7ade24167003b8488e10b6740a3" -dependencies = [ - "cfg-if", - "cloudabi", - "libc", - "redox_syscall", - "smallvec", - "winapi", -] - -[[package]] -name = "peeking_take_while" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" - -[[package]] -name = "percent-encoding" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" - -[[package]] -name = "ppv-lite86" -version = "0.2.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c36fa947111f5c62a733b652544dd0016a43ce89619538a8ef92724a6f501a20" - -[[package]] -name = "primitive-types" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c55c21c64d0eaa4d7ed885d959ef2d62d9e488c27c0e02d9aa5ce6c877b7d5f8" -dependencies = [ - "fixed-hash", - "uint", -] - -[[package]] -name = "proc-macro2" -version = "1.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12" -dependencies = [ - "unicode-xid", -] - -[[package]] -name = "prometheus" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0575e258dab62268e7236d7307caa38848acbda7ec7ab87bd9093791e999d20" -dependencies = [ - "cfg-if", - "fnv", - "lazy_static", - "protobuf", - "spin", - "thiserror", -] - -[[package]] -name = "protobuf" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb14183cc7f213ee2410067e1ceeadba2a7478a59432ff0747a335202798b1e2" - -[[package]] -name = "pwasm-utils" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f7a12f176deee919f4ba55326ee17491c8b707d0987aed822682c821b660192" -dependencies = [ - "byteorder", - "log", - "parity-wasm", -] - -[[package]] -name = "quick-error" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" - -[[package]] -name = "quickcheck" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44883e74aa97ad63db83c4bf8ca490f02b2fc02f92575e720c8551e843c945f" -dependencies = [ - "env_logger", - "log", - "rand", - "rand_core", -] - -[[package]] -name = "quickcheck_macros" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "608c156fd8e97febc07dc9c2e2c80bf74cfc6ef26893eae3daf8bc2bc94a4b7f" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "quote" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03" -dependencies = [ - "getrandom", - "libc", - "rand_chacha", - "rand_core", - "rand_hc", -] - -[[package]] -name = "rand_chacha" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19" -dependencies = [ - "getrandom", -] - -[[package]] -name = "rand_hc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c" -dependencies = [ - "rand_core", -] - -[[package]] -name = "redox_syscall" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce" - -[[package]] -name = "reed-solomon-erasure" -version = "4.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a415a013dd7c5d4221382329a5a3482566da675737494935cbbbcdec04662f9d" -dependencies = [ - "smallvec", -] - -[[package]] -name = "regex" -version = "1.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c3780fcf44b193bc4d09f36d2a3c87b251da4a046c87795a0d35f4f927ad8e6" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", - "thread_local", -] - -[[package]] -name = "regex-syntax" -version = "0.6.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26412eb97c6b088a6997e05f69403a802a92d520de2f8e63c2b65f9e0f47c4e8" - -[[package]] -name = "rocksdb" -version = "0.14.0" -source = "git+https://github.com/nearprotocol/rust-rocksdb?branch=disable-thread#44592812fd12cc2ea5de55af7c8d9bfdeacac692" -dependencies = [ - "libc", - "librocksdb-sys", -] - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hex" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e75f6a532d0fd9f7f13144f392b6ad56a32696bfcd9c78f797f16bbb6f072d6" - -[[package]] -name = "rustc_version" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" -dependencies = [ - "semver", -] - -[[package]] -name = "ryu" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" - -[[package]] -name = "scopeguard" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" - -[[package]] -name = "semver" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" -dependencies = [ - "semver-parser", -] - -[[package]] -name = "semver-parser" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" - -[[package]] -name = "serde" -version = "1.0.115" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e54c9a88f2da7238af84b5101443f0c0d0a3bbdc455e34a5c9497b1903ed55d5" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-bench" -version = "0.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d733da87e79faaac25616e33d26299a41143fd4cd42746cbb0e91d8feea243fd" -dependencies = [ - "byteorder", - "serde", -] - -[[package]] -name = "serde_bytes" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" -dependencies = [ - "serde", -] - -[[package]] -name = "serde_derive" -version = "1.0.115" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "609feed1d0a73cc36a0182a840a9b37b4a82f0b1150369f0536a9e3f2a31dc48" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "serde_json" -version = "1.0.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "164eacbdb13512ec2745fb09d51fd5b22b0d65ed294a1dcf7285a360c80a675c" -dependencies = [ - "indexmap", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha2" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a256f46ea78a0c0d9ff00077504903ac881a1dafdc20da66545699e7776b3e69" -dependencies = [ - "block-buffer", - "digest 0.8.1", - "fake-simd", - "opaque-debug", -] - -[[package]] -name = "sha3" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd26bc0e7a2e3a7c959bc494caf58b72ee0c71d67704e9520f736ca7e4853ecf" -dependencies = [ - "block-buffer", - "byte-tools", - "digest 0.8.1", - "keccak", - "opaque-debug", -] - -[[package]] -name = "shlex" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" - -[[package]] -name = "smallvec" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbee7696b84bbf3d89a1c2eccff0850e3047ed46bfcd2e92c29a2d074d57e252" - -[[package]] -name = "smart-default" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "133659a15339456eeeb07572eb02a91c91e9815e9cbc89566944d2c8d3efdbf6" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "spin" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "stream-cipher" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8131256a5896cabcf5eb04f4d6dacbe1aefda854b0d9896e09cb58829ec5638c" -dependencies = [ - "generic-array 0.12.3", -] - -[[package]] -name = "strsim" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" - -[[package]] -name = "strum" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57bd81eb48f4c437cadc685403cad539345bf703d78e63707418431cecd4522b" - -[[package]] -name = "strum_macros" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87c85aa3f8ea653bfd3ddf25f7ee357ee4d204731f6aa9ad04002306f6e2774c" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "subtle" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee" - -[[package]] -name = "subtle" -version = "2.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502d53007c02d7605a05df1c1a73ee436952781653da5d0bf57ad608f66932c1" - -[[package]] -name = "syn" -version = "1.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d8d6567fe7c7f8835a3a98af4208f3846fba258c1bc3c31d6e506239f11f9" -dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", -] - -[[package]] -name = "target-lexicon" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab0e7238dcc7b40a7be719a25365910f6807bd864f4cce6b2e6b873658e2b19d" - -[[package]] -name = "termcolor" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb6bfa289a4d7c5766392812c0a1f4c1ba45afa1ad47803c11e1f407d846d75f" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "textwrap" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060" -dependencies = [ - "unicode-width", -] - -[[package]] -name = "thiserror" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "thread_local" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d40c6d1b69745a6ec6fb1ca717914848da4b44ae29d9b3080cbee91d72a69b14" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "time" -version = "0.1.43" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca8a50ef2360fbd1eeb0ecd46795a87a19024eb4b53c5dc916ca1fd95fe62438" -dependencies = [ - "libc", - "winapi", -] - -[[package]] -name = "tinyvec" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "238ce071d267c5710f9d31451efec16c5ee22de34df17cc05e56cbc92e967117" - -[[package]] -name = "typenum" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "373c8a200f9e67a0c95e62a4f52fbf80c23b4381c05a17845531982fa99e6b33" - -[[package]] -name = "uint" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db035e67dfaf7edd9aebfe8676afcd63eed53c8a4044fed514c8cccf1835177" -dependencies = [ - "byteorder", - "crunchy", - "rustc-hex", - "static_assertions", -] - -[[package]] -name = "unicode-bidi" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" -dependencies = [ - "matches", -] - -[[package]] -name = "unicode-normalization" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb19cf769fa8c6a80a162df694621ebeb4dafb606470b2b2fce0be40a98a977" -dependencies = [ - "tinyvec", -] - -[[package]] -name = "unicode-segmentation" -version = "1.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83e153d1053cbb5a118eeff7fd5be06ed99153f00dbcd8ae310c5fb2b22edc0" - -[[package]] -name = "unicode-width" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3" - -[[package]] -name = "unicode-xid" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564" - -[[package]] -name = "url" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829d4a8476c35c9bf0bbce5a3b23f4106f79728039b726d292bb93bc106787cb" -dependencies = [ - "idna", - "matches", - "percent-encoding", -] - -[[package]] -name = "validator" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e60fadf92c22236de4028ceb0b8af50ed3430d41ad43d7a7d63b6bd1a8f47c38" -dependencies = [ - "idna", - "lazy_static", - "regex", - "serde", - "serde_derive", - "serde_json", - "url", -] - -[[package]] -name = "validator_derive" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d577dfb8ca9440a5c0b053d5a19b68f5c92ef57064bac87c8205c3f6072c20f" -dependencies = [ - "if_chain", - "lazy_static", - "proc-macro2", - "quote", - "regex", - "syn", - "validator", -] - -[[package]] -name = "vec_map" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" - -[[package]] -name = "version_check" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed" - -[[package]] -name = "void" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" - -[[package]] -name = "wasi" -version = "0.9.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519" - -[[package]] -name = "wasmer-runtime" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92a9ae96b193c35c47fc829265198322cf980edc353a9de32bc87a1545d44f3" -dependencies = [ - "lazy_static", - "memmap", - "serde", - "serde_derive", - "wasmer-runtime-core", - "wasmer-singlepass-backend", -] - -[[package]] -name = "wasmer-runtime-core" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740161245998752cf1a567e860fd6355df0336fedca6be1940ec7aaa59643220" -dependencies = [ - "bincode", - "blake3", - "cc", - "digest 0.8.1", - "errno", - "hex", - "indexmap", - "lazy_static", - "libc", - "nix", - "page_size", - "parking_lot", - "rustc_version", - "serde", - "serde-bench", - "serde_bytes", - "serde_derive", - "smallvec", - "target-lexicon", - "wasmparser", - "winapi", -] - -[[package]] -name = "wasmer-singlepass-backend" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a434ebf512ae1c76de46d41935a9ae10775fd937071334bdf5878cc41d9140" -dependencies = [ - "bincode", - "byteorder", - "dynasm", - "dynasmrt", - "lazy_static", - "libc", - "nix", - "serde", - "serde_derive", - "smallvec", - "wasmer-runtime-core", -] - -[[package]] -name = "wasmparser" -version = "0.51.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aeb1956b19469d1c5e63e459d29e7b5aa0f558d9f16fcef09736f8a265e6c10a" - -[[package]] -name = "which" -version = "3.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724" -dependencies = [ - "libc", -] - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "zeroize" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3cbac2ed2ba24cc90f5e06485ac8c7c1e5449fe8911aef4d8877218af021a5b8" diff --git a/near-sdk-sim/Cargo.toml b/near-sdk-sim/Cargo.toml deleted file mode 100644 index 198fc96d0..000000000 --- a/near-sdk-sim/Cargo.toml +++ /dev/null @@ -1,38 +0,0 @@ -[package] -name = "near-sdk-sim" -version = "4.0.0-pre.7" -authors = ["Near Inc "] -edition = "2018" -license = "GPL-3.0" -categories = ["wasm"] -repository = "https://github.com/near/near-sdk-rs" -homepage = "https://near-sdk.io" -description = """ -NEAR Simulator & cross-contract testing library -""" - - -[dependencies] -near-sdk = { path = "../near-sdk", version = "=4.0.0-pre.7" } -near-crypto = "=0.1.0" -near-primitives = "=0.1.0-pre.1" -near-vm-logic = "=4.0.0-pre.1" - -near-pool = "=0.1.0-pre.1" -near-store = "=0.1.0-pre.1" -near-runtime = "=4.0.0-pre.1" - -lazy-static-include = "3" -# Temporary workaround see https://github.com/bitvecto-rs/bitvec/issues/105 -funty = "=1.1.0" - -[dev-dependencies] -quickcheck = "0.9" -quickcheck_macros = "0.9" -fungible-token = { path="../examples/fungible-token/ft" } - -[features] -default = [] -no_cache = ["near-store/no_cache", "near-runtime/no_cache"] -no_sim = [] -no_contract_cache = [] diff --git a/near-sdk-sim/README.md b/near-sdk-sim/README.md deleted file mode 100644 index 9e6b48c4f..000000000 --- a/near-sdk-sim/README.md +++ /dev/null @@ -1,472 +0,0 @@ -# NEAR Simulator & cross-contract testing library - -When writing NEAR contracts, with Rust or other Wasm-compiled languages like [AssemblyScript](https://github.com/near/near-sdk-as), the default testing approach for your language of choice (such as `mod test` in your Rust project's `src/lib.rs` file) is great for testing the behavior of a single contract in isolation. - -But the true power of blockchains & smart contracts comes from cross-contract calls. How do you make sure your cross-contract code works as you expect? - -As a first step, you can use this library! With it, you can: - -* Test cross-contract calls -* Profile [gas](https://docs.near.org/docs/concepts/gas) & [storage](https://docs.near.org/docs/concepts/storage-staking) usage for your contract, establishing lower bounds for costs of deployed contracts and rapidly identifying problematic areas prior to deploying. -* Inspect intermediate state of all calls in a complicated chain of transactions - -To view this documentation locally, clone this repo and from this folder run `cargo doc --open`. - -## Changelog - -### `3.2.0` - -* Introduce `block_prod_time` duration in nanoseconds to `GenesisConfig` that defines the duration between produced blocks. -* Expose `cur_block` and `genesis_config` from `RuntimeStandalone`. This allows to manipulate block time. -* Use `RuntimeConfig::from_protocol_version` that fixes storage costs issue. -* Set root account balance to one billion tokens. - -# Getting started - -This section will guide you through our suggested approach to adding simulation tests to your project. Want an example? Check out the [Fungible Token Example](https://github.com/near/near-sdk-rs/tree/master/examples/fungible-token). - -## Dependency versions - -Currently this crate depends on a the GitHub repo of [nearcore](https://github.com/near/nearcore), so this crate must be a git dependency too. Furthermore, this crate's dependencies conflict with building the Wasm smart contract, so you must add it under the following: - -```toml -[dev-dependencies] -near-sdk-sim = "4.0.0-pre.6" - -``` - -And update `near-sdk` too: - -```toml -[dependencies] -near-sdk = "4.0.0-pre.6" - -``` - -Note that you need to add the `tag` (or `commit`) of the version. - - -## Workspace setup - -If you want to check gas & storage usage of one Rust contract, you can add the above dependencies to `Cargo.toml` in the root of your project. If you want to test cross-contract calls, we recommend setting up a cargo [workspace](https://doc.rust-lang.org/cargo/reference/workspaces.html). Here's how it works: - -Let's say you have an existing contract project in a folder called `contract`. - -Go ahead and make a subfolder within it called `contract`, and move the original contents of `contract` into this subfolder. Now you'll have `contract/contract`. You can rename the root folder to something like `contracts` or `contract-wrap`, if you want. Some bash commands to do this: - -```bash -mkdir contract-wrap -mv contract contract-wrap -``` - -Now in the root of the project (`contract-wrap`), create a new `Cargo.toml`. You'll have to add the normal `[package]` section, but unlike most projects you won't have any `dependencies`, only `dev-dependencies` and a `workspace`: - - -```toml -[dev-dependencies] -near-sdk = "4.0.0-pre.6" -near-sdk-sim = "4.0.0-pre.6" -contract = { path = "./contract" } - -[workspace] -members = [ - "contract" -] -``` - -Now when you want to create test contracts, you can add a new subfolder to your project and add a line for it to both `[dev-dependencies]` and `[workspace]` in this root `Cargo.toml`. - -Other cleanup: - -* You can move any `[profile.release]` settings from your nested project up to the root of the workspace, since workspace members inherit these settings from the workspace root. -* You can remove the nested project's `target`, since all workspace members will be built to the root project's `target` directory -* If you were building with `cargo build`, you can now build all workspace members at once with `cargo build --all` - - -## Test files - -In the root of your project (`contract-wrap` in the example above), create a `tests` directory with a Rust file inside. Anything in here will automatically be run by `cargo test`. - -Inside this folder, set up a new test crate for yourself by creating a `tests/sim` directory with a `tests/sim/main.rs` file. This file will glue together the other files (aka modules) in this folder. We'll add things to it soon. For now you can leave it empty. - -Now create a `tests/sim/utils.rs` file. This file will export common functions for all your tests. In it you need to include the bytes of the contract(s) you want to test: - -```rust -near_sdk_sim::lazy_static_include::lazy_static_include_bytes! { - // update `contract.wasm` for your contract's name - CONTRACT_WASM_BYTES => "target/wasm32-unknown-unknown/release/contract.wasm", - - // if you run `cargo build` without `--release` flag: - CONTRACT_WASM_BYTES => "target/wasm32-unknown-unknown/debug/contract.wasm", -} -``` - -Note that this means **you must `build` before you `test`!** Since `cargo test` does not re-generate the `wasm` files that your simulation tests rely on, you will need to `cargo build --all --target wasm32-unknown-unknown` before running `cargo test`. If you made contract changes and _you swear it should pass now_, try rebuilding! - -Now you can make a function to initialize your simulator: - -```rust -use near_sdk_sim::{init_simulator, to_yocto, STORAGE_AMOUNT}; - -const CONTRACT_ID: &str = "contract"; - -pub fn init() -> (UserAccount, UserAccount, UserAccount) { - // Use `None` for default genesis configuration; more info below - let root = init_simulator(None); - - let contract = root.deploy( - &CONTRACT_WASM_BYTES, - CONTRACT_ID.to_string(), - STORAGE_AMOUNT // attached deposit - ); - - let alice = root.create_user( - "alice".parse().unwrap(), - to_yocto("100") // initial balance - ); - - (root, contract, alice) -} -``` - -Now you can add a test file that uses this `init` function in `tests/sim/first_tests.rs`. For every file you add to this directory, you'll need to add a line to `tests/sim/main.rs`. Let's add one for both files so far: - -```rust -// in tests/sim/main.rs -mod utils; -mod first_tests; -``` - -Now add some tests to `first_tests.rs`: - -```rust -use near_sdk::serde_json::json; -use near_sdk_sim::DEFAULT_GAS; - -use crate::utils::init; - -#[test] -fn simulate_some_view_function() { - let (root, contract, _alice) = init(); - - let actual: String = root.view( - contract.account_id(), - "view_something", - &json!({ - "some_param": "some_value".to_string(), - }).to_string().into_bytes(), - ).unwrap_json(); - - assert_eq!("expected".to_string(), actual); -} - -#[test] -fn simulate_some_change_method() { - let (root, contract, _alice) = init(); - - let result = root.call( - contract.account_id(), - "change_something", - json!({ - "some_param": "some_value".to_string(), - }).to_string().into_bytes(), - DEFAULT_GAS, - 1, // deposit - ); - - assert!(result.is_ok()); -} -``` - - -## Optional macros - -The above approach is a good start, and will work even if your Wasm files are compiled from a language other than Rust. - -But if your original files are Rust and you want better ergonomics while testing, `near-sdk-sim` provides a nice bonus feature. - -`near-sdk-sim` modifies the `near_bindgen` macro from `near-sdk` to create an additional struct+implementation from your contract, with `Contract` added to the end of the name, like `xxxxxContract`. So if you have a contract with `[package].name` set to `token` with this in its `src/lib.rs`: - -```rust -#[near_bindgen] -struct Token { - ... -} - -#[near_bindgen] -impl Token { - ... -} -``` - -Then in your simulation test you can import `TokenContract`: - -```rust -use token::TokenContract; - -// or rename it maybe -use token::TokenContract as OtherNamedContract; -``` - -Now you can simplify the `init` & test code from the previous section: - -```rust -// in utils.rs -use near_sdk_sim::{deploy, init_simulator, to_yocto, STORAGE_AMOUNT}; -use token::TokenContract; - -const CONTRACT_ID: &str = "contract"; - -pub fn init() -> (UserAccount, ContractAccount, UserAccount) { - let root = init_simulator(None); - - let contract = deploy!( - contract: TokenContract, - contract_id: CONTRACT_ID, - bytes: &CONTRACT_WASM_BYTES, - signer_account: root - ); - - let alice = root.create_user( - "alice".parse().unwrap(), - to_yocto("100") // initial balance - ); - - (root, contract, alice) -} - -// in first_tests.rs -use near_sdk_sim::{call, view}; -use crate::utils::init; - -#[test] -fn simulate_some_view_function() { - let (root, contract, _alice) = init(); - - let actual: String = view!( - contract.view_something("some_value".to_string()), - ).unwrap_json(); - - assert_eq!("expected", actual); -} - -#[test] -fn simulate_some_change_method() { - let (root, contract, _alice) = init(); - - // uses default gas amount - let result = call!( - root, - contract.change_something("some_value".to_string()), - deposit = 1, - ); - - assert!(result.is_ok()); -} -``` - -# Common patterns - -## Profile gas costs - -For a chain of transactions kicked off by `call` or `call!`, you can check the `gas_burnt` and `tokens_burnt`, where `tokens_burnt` will equal `gas_burnt` multiplied by the `gas_price` set in the genesis config. You can also print out `profile_data` to see an in-depth gas-use breakdown. - -```rust -let outcome = some_account.call( - "some_contract", - "method", - &json({ - "some_param": "some_value", - }).to_string().into_bytes(), - DEFAULT_GAS, - 0, -); - -println!( - "profile_data: {:#?} \n\ntokens_burnt: {}Ⓝ", - outcome.profile_data(), - (outcome.tokens_burnt()) as f64 / 1e24 -); - -let expected_gas_ceiling = 5 * u64::pow(10, 12); // 5 TeraGas -assert!(outcome.gas_burnt() < expected_gas_ceiling); -``` - -TeraGas units are [explained here](https://docs.near.org/docs/concepts/gas#thinking-in-gas). - -Remember to run tests with `--nocapture` to see output from `println!`: - - cargo test -- --nocapture - -The output from this `println!` might look something like this: - - profile_data: ------------------------------ - Total gas: 1891395594588 - Host gas: 1595600369775 [84% total] - Action gas: 0 [0% total] - Wasm execution: 295795224813 [15% total] - ------ Host functions -------- - base -> 7678275219 [0% total, 0% host] - contract_compile_base -> 35445963 [0% total, 0% host] - contract_compile_bytes -> 48341969250 [2% total, 3% host] - read_memory_base -> 28708495200 [1% total, 1% host] - read_memory_byte -> 634822611 [0% total, 0% host] - write_memory_base -> 25234153749 [1% total, 1% host] - write_memory_byte -> 539306856 [0% total, 0% host] - read_register_base -> 20137321488 [1% total, 1% host] - read_register_byte -> 17938284 [0% total, 0% host] - write_register_base -> 25789702374 [1% total, 1% host] - write_register_byte -> 821137824 [0% total, 0% host] - utf8_decoding_base -> 3111779061 [0% total, 0% host] - utf8_decoding_byte -> 15162184908 [0% total, 0% host] - log_base -> 3543313050 [0% total, 0% host] - log_byte -> 686337132 [0% total, 0% host] - storage_write_base -> 192590208000 [10% total, 12% host] - storage_write_key_byte -> 1621105941 [0% total, 0% host] - storage_write_value_byte -> 2047223574 [0% total, 0% host] - storage_write_evicted_byte -> 2119742262 [0% total, 0% host] - storage_read_base -> 169070537250 [8% total, 10% host] - storage_read_key_byte -> 711908259 [0% total, 0% host] - storage_read_value_byte -> 370326330 [0% total, 0% host] - touching_trie_node -> 1046627135190 [55% total, 65% host] - ------ Actions -------- - ------------------------------ - - - tokens_burnt: 0.00043195379520539996Ⓝ - - -## Profile [storage](https://docs.near.org/docs/concepts/storage-staking) costs - -For a `ContractAccount` created with `deploy!` or a `UserAccount` created with `root.create_user`, you can call `account()` to get the [Account](https://github.com/near/nearcore/blob/df2d8bac977461c3abded5ef52ac3000f53e9097/core/primitives-core/src/account.rs#L8-L21) information stored in the simulated blockchain. - -```rs -let account = root.account().unwrap(); -let balance = account.amount; -let locked_in_stake = account.locked; -let storage_usage = account.storage_usage; -``` - -You can use this info to do detailed profiling of how contract calls alter the storage usage of accounts. - - -## Inspect intermediate state of all calls in a complicated chain of transactions - -Say you have a `call` or `call!`: - -```rust -let outcome = some_account.call( - "some_contract", - "method", - &json({ - "some_param": "some_value", - }).to_string().into_bytes(), - DEFAULT_GAS, - 0, -); -``` - -If `some_contract.method` here makes cross-contract calls, `near-sdk-sim` will allow all of these calls to complete. You can then inspect the entire chain of calls via the `outcome` struct. Some useful methods: - -* [`outcome.promise_results()`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L123-L126) -* [`outcome.get_receipt_results()`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L114-L117) -* [`outcome.logs()`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L156-L159) - -You can use these with `println!` and [pretty print interpolation](https://riptutorial.com/rust/example/1248/advanced-usage-of-println-): - -```rust -println!("{:#?}", outcome.promise_results); -``` - -Remember to run your tests with `--nocapture` to see the `println!` output: - - cargo test -- --nocapture - -You might see something like this: - - [ - Some( - ExecutionResult { - outcome: ExecutionOutcome { - logs: [], - receipt_ids: [ - `2bCDBfWgRkzGggXLuiXqhnVGbxwRz7RP3qa8WS5nNw8t`, - ], - burnt_gas: 2428220615156, - tokens_burnt: 0, - status: SuccessReceiptId(2bCDBfWgRkzGggXLuiXqhnVGbxwRz7RP3qa8WS5nNw8t), - }, - }, - ), - Some( - ExecutionResult { - outcome: ExecutionOutcome { - logs: [], - receipt_ids: [], - burnt_gas: 18841799405111, - tokens_burnt: 0, - status: Failure(Action #0: Smart contract panicked: panicked at 'Not an integer: ParseIntError { kind: InvalidDigit }', test-contract/src/lib.rs:85:56), - }, - }, - ) - ] - -You can see it's a little hard to tell which call is which, since the [ExecutionResult](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L20-L27) does not yet include the name of the contract or method. To help debug, you can use `log!` in your contract methods. All `log!` output will show up in the `logs` arrays in the ExecutionOutcomes shown above. - - -## Check expected transaction failures - -If you want to check something in the `logs` or `status` of one of the transactions in one of these call chains mentioned above, you can use string matching. To check that the Failure above matches your expectations, you could: - -```rust -use near_sdk_sim::transaction::ExecutionStatus; - -#[test] -fn simulate_some_failure() { - let outcome = some_account.call(...); - - assert_eq!(res.promise_errors().len(), 1); - - if let ExecutionStatus::Failure(execution_error) = - &outcome.promise_errors().remove(0).unwrap().outcome().status - { - assert!(execution_error.to_string().contains("ParseIntError")); - } else { - unreachable!(); - } -} -``` - -This [`promise_errors`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/near-sdk-sim/src/outcome.rs#L128-L135) returns a filtered version of the `promise_results` method mentioned above. - -Parsing `logs` is much simpler, whether [from `get_receipt_results`](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/examples/fungible-token/tests/sim/with_macros.rs#L128-L134) or [from `logs` directly](https://github.com/near/near-sdk-rs/blob/9cf75cf4a537a6f9906d82cfcadd97ae4a3443b6/examples/fungible-token/tests/sim/with_macros.rs#L70-L74). - - -# Tweaking the genesis config - -For many simulation tests, using `init_simulator(None)` is good enough. This uses the [default genesis configuration settings](https://github.com/near/near-sdk-rs/blob/0a9a56f1590e1f19efc974160c88f32efcb91ef4/near-sdk-sim/src/runtime.rs#L59-L72): - -```rust -GenesisConfig { - genesis_time: 0, - gas_price: 100_000_000, - gas_limit: std::u64::MAX, - genesis_height: 0, - epoch_length: 3, - runtime_config: RuntimeConfig::default(), - state_records: vec![], - validators: vec![], -} -``` - -If you want to override some of these values, for example to simulate how your contract will behave if [gas price](https://docs.near.org/docs/concepts/gas) goes up 10x: - -```rs -use near_sdk_sim::runtime::GenesisConfig; - -pub fn init () { - let mut genesis = GenesisConfig::default(); - genesis.gas_price = genesis.gas_price * 10; - let root = init_simulator(Some(genesis)); -} -``` diff --git a/near-sdk-sim/src/cache.rs b/near-sdk-sim/src/cache.rs deleted file mode 100644 index e0d4b3cca..000000000 --- a/near-sdk-sim/src/cache.rs +++ /dev/null @@ -1,91 +0,0 @@ -use crate::types::CompiledContractCache; -use std::collections::HashMap; -use std::fs::{File, OpenOptions}; -use std::io::{Read, Write}; -use std::path::{Path, PathBuf}; -use std::sync::{Arc, Mutex}; - -/// This provides a disc cache for compiled contracts. -/// The cached contracts are located `CARGO_MANIFEST_DIR/target/contract_cache`. -#[derive(Clone, Default)] -pub struct ContractCache { - data: Arc, Vec>>>, -} - -pub(crate) fn key_to_b58(key: &[u8]) -> String { - near_sdk::bs58::encode(key).into_string() -} - -impl ContractCache { - pub fn new() -> Self { - ContractCache::default() - } - - fn path() -> PathBuf { - let s = std::env::var("CARGO_MANIFEST_DIR").unwrap(); - Path::new(&s).join("target").join("contract_cache") - } - - fn open_file(&self, key: &[u8]) -> std::io::Result { - let path = self.get_path(key); - // Ensure that the parent path exists - let prefix = path.parent().unwrap(); - std::fs::create_dir_all(prefix).unwrap(); - // Ensure we can read, write, and create file if it doesn't exist - OpenOptions::new().read(true).write(true).create(true).open(path) - } - - fn get_path(&self, key: &[u8]) -> PathBuf { - ContractCache::path().join(key_to_b58(key)) - } - - fn file_exists(&self, key: &[u8]) -> bool { - self.get_path(key).exists() - } - - pub fn insert(&self, key: &[u8], value: &[u8]) -> Option> { - self.data.lock().unwrap().insert(key.to_vec(), value.to_vec()) - } - - pub fn get(&self, key: &[u8]) -> Option> { - self.data.lock().unwrap().get(key).cloned() - } - - #[allow(dead_code)] - pub(crate) fn to_arc(&self) -> Arc { - Arc::new(self.clone()) - } -} - -impl CompiledContractCache for ContractCache { - fn put(&self, key: &[u8], value: &[u8]) -> Result<(), std::io::Error> { - self.insert(key, value); - let mut file = self.open_file(key).expect("File failed to open"); - let metadata = file.metadata()?; - if metadata.len() != value.len() as u64 { - file.write_all(value)?; - } - Ok(()) - } - - fn get(&self, key: &[u8]) -> Result>, std::io::Error> { - if (*self.data).lock().unwrap().contains_key(key) { - return Ok(self.get(key)); - } else if self.file_exists(key) { - let mut file = self.open_file(key)?; - let mut contents = vec![]; - file.read_to_end(&mut contents)?; - self.insert(key, &contents); - return Ok(Some(contents)); - } - Ok(None) - } -} - -pub fn create_cache() -> ContractCache { - ContractCache::new() -} - -pub fn cache_to_arc(cache: &ContractCache) -> Arc { - cache.to_arc() -} diff --git a/near-sdk-sim/src/lib.rs b/near-sdk-sim/src/lib.rs deleted file mode 100644 index eee2f9b08..000000000 --- a/near-sdk-sim/src/lib.rs +++ /dev/null @@ -1,24 +0,0 @@ -//! # near_sdk_sim -//! -//! This crate provides an interface for simulating transactions on NEAR's Blockchain. -//! The simulator uses a standalone runtime that can handle any of the [actions](https://nomicon.io/RuntimeSpec/Actions.html) provided by the -//! real runtime, including: creating accounts, deploying contracts, making contract calls and -//! calling view methods. - -pub mod outcome; -#[doc(inline)] -pub use outcome::*; -mod cache; -pub mod runtime; -pub mod units; -pub mod user; -pub use near_crypto; -#[doc(hidden)] -pub use near_primitives::*; -#[doc(inline)] -pub use units::*; -#[doc(inline)] -pub use user::*; - -#[doc(hidden)] -pub use lazy_static_include; diff --git a/near-sdk-sim/src/outcome.rs b/near-sdk-sim/src/outcome.rs deleted file mode 100644 index 40f3e2f64..000000000 --- a/near-sdk-sim/src/outcome.rs +++ /dev/null @@ -1,259 +0,0 @@ -use crate::hash::CryptoHash; -use crate::runtime::{init_runtime, RuntimeStandalone}; -use crate::transaction::{ExecutionOutcome, ExecutionStatus}; -use core::fmt; -use near_primitives::profile::ProfileData; -use near_primitives::transaction::ExecutionStatus::{SuccessReceiptId, SuccessValue}; -use near_primitives::types::AccountId; -use near_sdk::borsh::BorshDeserialize; -use near_sdk::serde::de::DeserializeOwned; -use near_sdk::serde_json::Value; -use near_sdk::Gas; -use std::borrow::Borrow; -use std::cell::RefCell; -use std::fmt::Debug; -use std::fmt::Formatter; -use std::rc::Rc; - -pub type TxResult = Result; - -/// An ExecutionResult is created by a UserAccount submitting a transaction. -/// It wraps an ExecutionOutcome which is the same object returned from an RPC call. -#[derive(Clone)] -pub struct ExecutionResult { - runtime: Rc>, - outcome: ExecutionOutcome, - hash: CryptoHash, -} - -impl Debug for ExecutionResult { - fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { - f.debug_struct("ExecutionResult").field("outcome", &self.outcome).finish() - } -} - -impl Default for ExecutionResult { - fn default() -> Self { - ExecutionResult::new( - ExecutionOutcome::default(), - &Rc::new(RefCell::new(init_runtime(None).0)), - CryptoHash::default(), - ) - } -} - -impl ExecutionResult { - #[doc(hidden)] - pub fn new( - outcome: ExecutionOutcome, - runtime: &Rc>, - hash: CryptoHash, - ) -> Self { - Self { runtime: Rc::clone(runtime), outcome, hash } - } - - /// Interpret the SuccessValue as a JSON value - pub fn unwrap_json_value(&self) -> Value { - use crate::transaction::ExecutionStatus::*; - match &(self.outcome).status { - SuccessValue(s) => near_sdk::serde_json::from_slice(s).unwrap(), - err => panic!("Expected Success value but got: {:#?}", err), - } - } - - /// Deserialize SuccessValue from Borsh - pub fn unwrap_borsh(&self) -> T { - use crate::transaction::ExecutionStatus::*; - match &(self.outcome).status { - SuccessValue(s) => BorshDeserialize::try_from_slice(s).unwrap(), - _ => panic!("Cannot get value of failed transaction"), - } - } - - /// Deserialize SuccessValue from JSON - pub fn unwrap_json(&self) -> T { - near_sdk::serde_json::from_value(self.unwrap_json_value()).unwrap() - } - - /// Check if transaction was successful - pub fn is_ok(&self) -> bool { - matches!(&(self.outcome).status, SuccessValue(_) | SuccessReceiptId(_)) - } - - /// Test whether there is a SuccessValue - pub fn has_value(&self) -> bool { - matches!(self.outcome.status, SuccessValue(_)) - } - - /// Asserts that the outcome is successful - pub fn assert_success(&self) { - assert!(self.is_ok(), "Outcome {:#?} was a failure", self.outcome); - } - - /// Lookup an execution result from a hash - pub fn lookup_hash(&self, hash: &CryptoHash) -> Option { - self.get_outcome(hash) - } - - fn get_outcome(&self, hash: &CryptoHash) -> Option { - (*self.runtime) - .borrow() - .outcome(hash) - .map(|out| ExecutionResult::new(out, &self.runtime, *hash)) - } - - /// Reference to internal ExecutionOutcome - pub fn outcome(&self) -> &ExecutionOutcome { - &self.outcome - } - - /// Return results of promises from the `receipt_ids` in the ExecutionOutcome - pub fn get_receipt_results(&self) -> Vec> { - self.get_outcomes(&self.outcome.receipt_ids) - } - - fn get_outcomes(&self, ids: &[CryptoHash]) -> Vec> { - ids.iter().map(|id| self.get_outcome(id)).collect() - } - - /// Return the results of any promises created since the last transaction - pub fn promise_results(&self) -> Vec> { - self.get_outcomes(&(*self.runtime).borrow().last_outcomes) - } - - pub fn promise_errors(&self) -> Vec> { - let mut res = self.promise_results(); - res.retain(|outcome| match outcome { - Some(o) => !o.is_ok(), - _ => false, - }); - res - } - - /// Execution status. Contains the result in case of successful execution. - /// NOTE: Should be the latest field since it contains unparsable by light client - /// ExecutionStatus::Failure - pub fn status(&self) -> ExecutionStatus { - self.outcome.status.clone() - } - - /// The amount of the gas burnt by the given transaction or receipt. - pub fn gas_burnt(&self) -> Gas { - Gas(self.outcome.gas_burnt) - } - - /// The amount of tokens burnt corresponding to the burnt gas amount. - /// This value doesn't always equal to the `gas_burnt` multiplied by the gas price, because - /// the prepaid gas price might be lower than the actual gas price and it creates a deficit. - pub fn tokens_burnt(&self) -> u128 { - self.outcome.tokens_burnt - } - - /// Logs from this transaction or receipt. - pub fn logs(&self) -> &Vec { - &self.outcome.logs - } - - /// The id of the account on which the execution happens. For transaction this is signer_id, - /// for receipt this is receiver_id. - pub fn executor_id(&self) -> &AccountId { - &self.outcome.executor_id - } - - /// Receipt IDs generated by this transaction or receipt. - pub fn receipt_ids(&self) -> &Vec { - &self.outcome.receipt_ids - } - - pub fn profile_data(&self) -> ProfileData { - (*self.runtime).borrow().profile_of_outcome(&self.hash).unwrap() - } -} - -#[doc(hidden)] -pub fn outcome_into_result( - outcome: (CryptoHash, ExecutionOutcome), - runtime: &Rc>, -) -> ExecutionResult { - match (outcome.1).status { - ExecutionStatus::SuccessValue(_) | - ExecutionStatus::Failure(_) => ExecutionResult::new(outcome.1, runtime, outcome.0), - ExecutionStatus::SuccessReceiptId(_) => panic!("Unresolved ExecutionOutcome run runtime.resolve(tx) to resolve the final outcome of tx"), - ExecutionStatus::Unknown => unreachable!() - } -} - -/// The result of a view call. Contains the logs made during the view method call and Result value, -/// which can be unwrapped and deserialized. -#[derive(Debug)] -pub struct ViewResult { - result: Result, Box>, - logs: Vec, -} - -impl ViewResult { - pub fn new(result: Result, Box>, logs: Vec) -> Self { - Self { result, logs } - } - - /// Logs made during the view call - pub fn logs(&self) -> &Vec { - &self.logs - } - - pub fn is_err(&self) -> bool { - self.result.is_err() - } - - pub fn is_ok(&self) -> bool { - self.result.is_ok() - } - - /// Attempt unwrap the value returned by the view call and panic if it is an error - pub fn unwrap(&self) -> Vec { - (&self.result).as_ref().borrow().unwrap().clone() - } - - pub fn unwrap_err(&self) -> &dyn std::error::Error { - (&self.result).as_ref().borrow().unwrap_err().as_ref().borrow() - } - - /// Interpret the value as a JSON::Value - pub fn unwrap_json_value(&self) -> Value { - near_sdk::serde_json::from_slice(self.result.as_ref().expect("ViewResult is an error")) - .unwrap() - } - - /// Deserialize the value with Borsh - pub fn unwrap_borsh(&self) -> T { - BorshDeserialize::try_from_slice(self.result.as_ref().expect("ViewResult is an error")) - .unwrap() - } - - /// Deserialize the value with JSON - pub fn unwrap_json(&self) -> T { - near_sdk::serde_json::from_value(self.unwrap_json_value()).unwrap() - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::runtime::init_runtime; - use near_primitives::transaction::ExecutionStatus::SuccessValue; - use near_sdk::serde_json::json; - - #[test] - fn value_test() { - let value = json!({ - "id": "hello" - }); - let status = SuccessValue(value.to_string().as_bytes().to_vec()); - let outcome = ExecutionOutcome { status, ..Default::default() }; - let result = outcome_into_result( - (CryptoHash::default(), outcome), - &Rc::new(RefCell::new(init_runtime(None).0)), - ); - assert_eq!(value, result.unwrap_json_value()); - } -} diff --git a/near-sdk-sim/src/runtime.rs b/near-sdk-sim/src/runtime.rs deleted file mode 100644 index 41bacc7e2..000000000 --- a/near-sdk-sim/src/runtime.rs +++ /dev/null @@ -1,548 +0,0 @@ -use std::collections::HashMap; -use std::sync::Arc; - -use crate::cache::{cache_to_arc, create_cache, ContractCache}; -use crate::ViewResult; -use near_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; -use near_pool::{types::PoolIterator, TransactionPool}; -use near_primitives::account::{AccessKey, Account}; -use near_primitives::errors::RuntimeError; -use near_primitives::hash::CryptoHash; -use near_primitives::profile::ProfileData; -use near_primitives::receipt::Receipt; -use near_primitives::runtime::config::RuntimeConfig; -use near_primitives::state_record::StateRecord; -use near_primitives::test_utils::account_new; -use near_primitives::test_utils::MockEpochInfoProvider; -use near_primitives::transaction::{ExecutionOutcome, ExecutionStatus, SignedTransaction}; -use near_primitives::types::{ - AccountInfo, Balance, BlockHeight, EpochHeight, EpochId, EpochInfoProvider, Gas, - StateChangeCause, -}; -use near_primitives::version::PROTOCOL_VERSION; -use near_primitives::views::ViewApplyState; -use near_runtime::{state_viewer::TrieViewer, ApplyState, Runtime}; -use near_sdk::{AccountId, Duration}; -use near_store::{ - get_access_key, get_account, set_account, test_utils::create_test_store, ShardTries, Store, -}; - -const DEFAULT_EPOCH_LENGTH: u64 = 3; -const DEFAULT_BLOCK_PROD_TIME: Duration = 1_000_000_000; - -pub fn init_runtime( - genesis_config: Option, -) -> (RuntimeStandalone, InMemorySigner, AccountId) { - let mut genesis = genesis_config.unwrap_or_default(); - genesis.runtime_config.wasm_config.limit_config.max_total_prepaid_gas = genesis.gas_limit; - let root_account_id: AccountId = AccountId::new_unchecked("root".to_string()); - let signer = genesis.init_root_signer(root_account_id.as_str()); - let runtime = RuntimeStandalone::new_with_store(genesis); - (runtime, signer, root_account_id) -} - -#[derive(Debug)] -pub struct GenesisConfig { - pub genesis_time: u64, - pub gas_price: Balance, - pub gas_limit: Gas, - pub genesis_height: u64, - pub epoch_length: u64, - pub block_prod_time: Duration, - pub runtime_config: RuntimeConfig, - pub state_records: Vec, - pub validators: Vec, -} - -impl Default for GenesisConfig { - fn default() -> Self { - let runtime_config = RuntimeConfig::from_protocol_version( - &Arc::new(RuntimeConfig::default()), - PROTOCOL_VERSION, - ) - .as_ref() - .clone(); - Self { - genesis_time: 0, - gas_price: 100_000_000, - gas_limit: runtime_config.wasm_config.limit_config.max_total_prepaid_gas, - genesis_height: 0, - epoch_length: DEFAULT_EPOCH_LENGTH, - block_prod_time: DEFAULT_BLOCK_PROD_TIME, - runtime_config, - state_records: vec![], - validators: vec![], - } - } -} - -impl GenesisConfig { - pub fn init_root_signer(&mut self, account_id: &str) -> InMemorySigner { - let signer = InMemorySigner::from_seed(account_id, KeyType::ED25519, "test"); - let root_account = account_new(10u128.pow(33), CryptoHash::default()); - - self.state_records.push(StateRecord::Account { - account_id: account_id.to_string(), - account: root_account, - }); - self.state_records.push(StateRecord::AccessKey { - account_id: account_id.to_string(), - public_key: signer.public_key(), - access_key: AccessKey::full_access(), - }); - signer - } -} - -#[derive(Debug, Default, Clone)] -pub struct Block { - prev_block: Option>, - state_root: CryptoHash, - pub epoch_height: EpochHeight, - pub block_height: BlockHeight, - pub block_timestamp: u64, - pub gas_price: Balance, - pub gas_limit: Gas, -} - -impl Drop for Block { - fn drop(&mut self) { - // Blocks form a liked list, so the generated recursive drop overflows - // the stack. Let's use an explicit loop to avoid that. - let mut curr = self.prev_block.take(); - while let Some(mut next) = curr.and_then(|it| Arc::try_unwrap(it).ok()) { - curr = next.prev_block.take(); - } - } -} - -impl Block { - pub fn genesis(genesis_config: &GenesisConfig) -> Self { - Self { - prev_block: None, - state_root: CryptoHash::default(), - block_height: genesis_config.genesis_height, - epoch_height: 0, - block_timestamp: genesis_config.genesis_time, - gas_price: genesis_config.gas_price, - gas_limit: genesis_config.gas_limit, - } - } - - fn produce( - &self, - new_state_root: CryptoHash, - epoch_length: u64, - block_prod_time: Duration, - ) -> Block { - Self { - gas_price: self.gas_price, - gas_limit: self.gas_limit, - block_timestamp: self.block_timestamp + block_prod_time, - prev_block: Some(Arc::new(self.clone())), - state_root: new_state_root, - block_height: self.block_height + 1, - epoch_height: (self.block_height + 1) / epoch_length, - } - } -} - -pub struct RuntimeStandalone { - pub genesis: GenesisConfig, - tx_pool: TransactionPool, - transactions: HashMap, - outcomes: HashMap, - profile: HashMap, - pub cur_block: Block, - runtime: Runtime, - tries: ShardTries, - pending_receipts: Vec, - epoch_info_provider: Box, - pub last_outcomes: Vec, - cache: ContractCache, -} - -impl RuntimeStandalone { - pub fn new(genesis: GenesisConfig, store: Arc) -> Self { - let mut genesis_block = Block::genesis(&genesis); - let mut store_update = store.store_update(); - let runtime = Runtime::new(); - let tries = ShardTries::new(store, 1); - let (s_update, state_root) = runtime.apply_genesis_state( - tries.clone(), - 0, - &[], - &genesis.state_records, - &genesis.runtime_config, - ); - store_update.merge(s_update); - store_update.commit().unwrap(); - genesis_block.state_root = state_root; - let validators = genesis.validators.clone(); - Self { - genesis, - tries, - runtime, - transactions: HashMap::new(), - outcomes: HashMap::new(), - profile: HashMap::new(), - cur_block: genesis_block, - tx_pool: TransactionPool::new(), - pending_receipts: vec![], - epoch_info_provider: Box::new(MockEpochInfoProvider::new( - validators.into_iter().map(|info| (info.account_id, info.amount)), - )), - cache: create_cache(), - last_outcomes: vec![], - } - } - - pub fn new_with_store(genesis: GenesisConfig) -> Self { - RuntimeStandalone::new(genesis, create_test_store()) - } - - /// Processes blocks until the final value is produced - pub fn resolve_tx( - &mut self, - mut tx: SignedTransaction, - ) -> Result<(CryptoHash, ExecutionOutcome), RuntimeError> { - tx.init(); - let mut outcome_hash = tx.get_hash(); - self.transactions.insert(outcome_hash, tx.clone()); - self.tx_pool.insert_transaction(tx); - self.last_outcomes = vec![]; - loop { - self.produce_block()?; - if let Some(outcome) = self.outcomes.get(&outcome_hash) { - match outcome.status { - ExecutionStatus::Unknown => unreachable!(), // ExecutionStatus::Unknown is not relevant for a standalone runtime - ExecutionStatus::SuccessReceiptId(ref id) => outcome_hash = *id, - ExecutionStatus::SuccessValue(_) | ExecutionStatus::Failure(_) => { - return Ok((outcome_hash, outcome.clone())) - } - }; - } else if self.pending_receipts.is_empty() { - unreachable!("Lost an outcome for the receipt hash {}", outcome_hash); - } - } - } - - /// Just puts tx into the transaction pool - pub fn send_tx(&mut self, tx: SignedTransaction) -> CryptoHash { - let tx_hash = tx.get_hash(); - self.transactions.insert(tx_hash, tx.clone()); - self.tx_pool.insert_transaction(tx); - tx_hash - } - - pub fn outcome(&self, hash: &CryptoHash) -> Option { - self.outcomes.get(hash).cloned() - } - - pub fn profile_of_outcome(&self, hash: &CryptoHash) -> Option { - self.profile.get(hash).cloned() - } - - /// Processes all transactions and pending receipts until there is no pending_receipts left - pub fn process_all(&mut self) -> Result<(), RuntimeError> { - loop { - self.produce_block()?; - if self.pending_receipts.is_empty() { - return Ok(()); - } - } - } - - /// Processes one block. Populates outcomes and producining new pending_receipts. - pub fn produce_block(&mut self) -> Result<(), RuntimeError> { - let profile_data = ProfileData::default(); - let apply_state = ApplyState { - block_index: self.cur_block.block_height, - prev_block_hash: Default::default(), - epoch_height: self.cur_block.epoch_height, - gas_price: self.cur_block.gas_price, - block_timestamp: self.cur_block.block_timestamp, - gas_limit: None, - // not used - random_seed: Default::default(), - epoch_id: EpochId::default(), - current_protocol_version: PROTOCOL_VERSION, - config: Arc::new(self.genesis.runtime_config.clone()), - #[cfg(feature = "no_contract_cache")] - cache: None, - #[cfg(not(feature = "no_contract_cache"))] - cache: Some(cache_to_arc(&self.cache)), - profile: profile_data.clone(), - block_hash: Default::default(), - }; - - let apply_result = self.runtime.apply( - self.tries.get_trie_for_shard(0), - self.cur_block.state_root, - &None, - &apply_state, - &self.pending_receipts, - &Self::prepare_transactions(&mut self.tx_pool), - self.epoch_info_provider.as_ref(), - )?; - self.pending_receipts = apply_result.outgoing_receipts; - apply_result.outcomes.iter().for_each(|outcome| { - self.last_outcomes.push(outcome.id); - self.outcomes.insert(outcome.id, outcome.outcome.clone()); - self.profile.insert(outcome.id, profile_data.clone()); - }); - let (update, _) = - self.tries.apply_all(&apply_result.trie_changes, 0).expect("Unexpected Storage error"); - update.commit().expect("Unexpected io error"); - self.cur_block = self.cur_block.produce( - apply_result.state_root, - self.genesis.epoch_length, - self.genesis.block_prod_time, - ); - - Ok(()) - } - - /// Produce num_of_blocks blocks. - /// # Examples - /// - /// ``` - /// use near_sdk_sim::runtime::init_runtime; - /// let (mut runtime, _, _) = init_runtime(None); - /// runtime.produce_blocks(5); - /// assert_eq!(runtime.current_block().block_height, 5); - /// assert_eq!(runtime.current_block().epoch_height, 1); - ///``` - - pub fn produce_blocks(&mut self, num_of_blocks: u64) -> Result<(), RuntimeError> { - for _ in 0..num_of_blocks { - self.produce_block()?; - } - Ok(()) - } - - /// Force alter account and change state_root. - pub fn force_account_update(&mut self, account_id: AccountId, account: &Account) { - let mut trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); - set_account(&mut trie_update, String::from(account_id), account); - trie_update.commit(StateChangeCause::ValidatorAccountsUpdate); - let (trie_changes, _) = trie_update.finalize().expect("Unexpected Storage error"); - let (store_update, new_root) = self.tries.apply_all(&trie_changes, 0).unwrap(); - store_update.commit().expect("No io errors expected"); - self.cur_block.state_root = new_root; - } - - pub fn view_account(&self, account_id: &str) -> Option { - let trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); - get_account(&trie_update, &account_id.to_string()).expect("Unexpected Storage error") - } - - pub fn view_access_key(&self, account_id: &str, public_key: &PublicKey) -> Option { - let trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); - get_access_key(&trie_update, &account_id.to_string(), public_key) - .expect("Unexpected Storage error") - } - - /// Returns a ViewResult containing the value or error and any logs - pub fn view_method_call( - &self, - account_id: &str, - function_name: &str, - args: &[u8], - ) -> ViewResult { - let trie_update = self.tries.new_trie_update(0, self.cur_block.state_root); - let viewer = TrieViewer {}; - let mut logs = vec![]; - let view_state = ViewApplyState { - block_height: self.cur_block.block_height, - prev_block_hash: self.cur_block.prev_block.as_ref().unwrap().state_root, - epoch_id: EpochId::default(), - epoch_height: self.cur_block.epoch_height, - block_timestamp: self.cur_block.block_timestamp, - current_protocol_version: PROTOCOL_VERSION, - cache: Some(cache_to_arc(&self.cache)), - block_hash: self.cur_block.state_root, - }; - let result = viewer.call_function( - trie_update, - view_state, - &account_id.to_string(), - function_name, - args, - &mut logs, - self.epoch_info_provider.as_ref(), - ); - ViewResult::new(result, logs) - } - - /// Returns a reference to the current block. - /// - /// # Examples - /// ``` - /// use near_sdk_sim::runtime::init_runtime; - /// let (mut runtime, _, _) = init_runtime(None); - /// runtime.produce_block().unwrap(); - /// runtime.current_block(); - /// assert_eq!(runtime.current_block().block_height, 1); - /// runtime.produce_blocks(4).unwrap(); - /// assert_eq!(runtime.current_block().block_height, 5); - /// ``` - pub fn current_block(&self) -> &Block { - &self.cur_block - } - - pub fn pending_receipts(&self) -> &[Receipt] { - &self.pending_receipts - } - - fn prepare_transactions(tx_pool: &mut TransactionPool) -> Vec { - let mut res = vec![]; - let mut pool_iter = tx_pool.pool_iterator(); - while let Some(iter) = pool_iter.next() { - if let Some(tx) = iter.next() { - res.push(tx); - } - } - res - } -} - -#[cfg(test)] -mod tests { - use super::*; - use crate::to_yocto; - - struct Foo {} - - impl Foo { - fn _private(&self) { - print!("yay!") - } - } - - #[test] - fn single_test() { - let _foo = Foo {}; - _foo._private(); - } - - #[test] - fn single_block() { - let (mut runtime, signer, _) = init_runtime(None); - let hash = runtime.send_tx(SignedTransaction::create_account( - 1, - signer.account_id.clone(), - "alice".into(), - 100, - signer.public_key(), - &signer, - CryptoHash::default(), - )); - runtime.produce_block().unwrap(); - assert!(matches!( - runtime.outcome(&hash), - Some(ExecutionOutcome { status: ExecutionStatus::SuccessReceiptId(_), .. }) - )); - } - - #[test] - fn process_all() { - let (mut runtime, signer, _) = init_runtime(None); - assert_eq!(runtime.view_account("alice"), None); - let outcome = runtime.resolve_tx(SignedTransaction::create_account( - 1, - signer.account_id.clone(), - "alice".into(), - 165437999999999999999000, - signer.public_key(), - &signer, - CryptoHash::default(), - )); - assert!(matches!( - outcome, - Ok((_, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })) - )); - assert_eq!( - runtime.view_account("alice"), - Some(Account { - amount: 165437999999999999999000, - code_hash: CryptoHash::default(), - locked: 0, - storage_usage: 182, - }) - ); - } - - #[test] - fn test_cross_contract_call() { - let (mut runtime, signer, _) = init_runtime(None); - - assert!(matches!( - runtime.resolve_tx(SignedTransaction::create_contract( - 1, - signer.account_id.clone(), - "status".into(), - include_bytes!("../../examples/status-message/res/status_message.wasm") - .as_ref() - .into(), - to_yocto("35"), - signer.public_key(), - &signer, - CryptoHash::default(), - )), - Ok((_, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })) - )); - let res = runtime.resolve_tx(SignedTransaction::create_contract( - 2, - signer.account_id.clone(), - "caller".into(), - include_bytes!("../../examples/factory-contract/res/factory_contract_high_level.wasm") - .as_ref() - .into(), - to_yocto("35"), - signer.public_key(), - &signer, - CryptoHash::default(), - )); - assert!(matches!( - res, - Ok((_, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })) - )); - let res = runtime.resolve_tx(SignedTransaction::call( - 3, - signer.account_id.clone(), - "caller".into(), - &signer, - 0, - "simple_call".into(), - "{\"account_id\": \"status\", \"message\": \"caller status is ok!\"}" - .as_bytes() - .to_vec(), - 300_000_000_000_000, - CryptoHash::default(), - )); - let (_, res) = res.unwrap(); - runtime.process_all().unwrap(); - - assert!(matches!(res, ExecutionOutcome { status: ExecutionStatus::SuccessValue(_), .. })); - let res = runtime.view_method_call("status", "get_status", b"{\"account_id\": \"root\"}"); - - let caller_status = String::from_utf8(res.unwrap()).unwrap(); - assert_eq!("\"caller status is ok!\"", caller_status); - } - - #[test] - fn test_force_update_account() { - let (mut runtime, _, _) = init_runtime(None); - let mut bob_account = runtime.view_account("root").unwrap(); - bob_account.locked = 10000; - runtime.force_account_update("root".parse().unwrap(), &bob_account); - assert_eq!(runtime.view_account("root").unwrap().locked, 10000); - } - - #[test] - fn can_produce_many_blocks_without_stack_overflow() { - let (mut runtime, _signer, _) = init_runtime(None); - runtime.produce_blocks(20_000).unwrap(); - } -} diff --git a/near-sdk-sim/src/units.rs b/near-sdk-sim/src/units.rs deleted file mode 100644 index d48d3a704..000000000 --- a/near-sdk-sim/src/units.rs +++ /dev/null @@ -1,20 +0,0 @@ -pub fn to_nanos(num_days: u64) -> u64 { - num_days * 86_400_000_000_000 -} - -pub fn to_ts(num_days: u64) -> u64 { - // 2018-08-01 UTC in nanoseconds - 1_533_081_600_000_000_000 + to_nanos(num_days) -} - -pub fn to_yocto(value: &str) -> u128 { - let vals: Vec<_> = value.split('.').collect(); - let part1 = vals[0].parse::().unwrap() * 10u128.pow(24); - if vals.len() > 1 { - let power = vals[1].len() as u32; - let part2 = vals[1].parse::().unwrap() * 10u128.pow(24 - power); - part1 + part2 - } else { - part1 - } -} diff --git a/near-sdk-sim/src/user.rs b/near-sdk-sim/src/user.rs deleted file mode 100644 index 5fef1f43b..000000000 --- a/near-sdk-sim/src/user.rs +++ /dev/null @@ -1,555 +0,0 @@ -use std::cell::{Ref, RefCell, RefMut}; -use std::fmt::{Debug, Formatter}; -use std::rc::Rc; - -use near_crypto::{InMemorySigner, KeyType, PublicKey, Signer}; - -use near_sdk::AccountId; -use near_sdk::PendingContractTx; - -use crate::runtime::init_runtime; -pub use crate::to_yocto; -use crate::{ - account::{AccessKey, Account}, - hash::CryptoHash, - outcome_into_result, - runtime::{GenesisConfig, RuntimeStandalone}, - transaction::Transaction, - types::{Balance, Gas}, - ExecutionResult, ViewResult, -}; - -pub const DEFAULT_GAS: u64 = 300_000_000_000_000; -pub const STORAGE_AMOUNT: u128 = 50_000_000_000_000_000_000_000_000; - -type Runtime = Rc>; - -/// A transaction to be signed by the user which created it. Multiple actions can be chained together -/// and then signed and sumited to be executed. -/// -/// # Example: -/// -/// ``` -/// use near_sdk_sim::{to_yocto, account::AccessKey}; -/// use near_crypto::{InMemorySigner, KeyType, Signer}; -/// let master_account = near_sdk_sim::init_simulator(None); -/// let account_id = "alice"; -/// let transaction = master_account.create_transaction(account_id.parse().unwrap()); -/// // Creates a signer which contains a public key. -/// let signer = InMemorySigner::from_seed(account_id, KeyType::ED25519, account_id); -/// let res = transaction.create_account() -/// .add_key(signer.public_key(), AccessKey::full_access()) -/// .transfer(to_yocto("10")) -/// .submit(); -/// ``` -/// -/// This creates an account for `alice`, and a new key pair for the account, adding the -/// public key to the account, and finally transfering `10` NEAR to the account from the -/// `master_account`. -/// -pub struct UserTransaction { - transaction: Transaction, - signer: InMemorySigner, - runtime: Runtime, -} - -impl UserTransaction { - /// Sign and execute the transaction - pub fn submit(self) -> ExecutionResult { - let res = - (*self.runtime).borrow_mut().resolve_tx(self.transaction.sign(&self.signer)).unwrap(); - (*self.runtime).borrow_mut().process_all().unwrap(); - outcome_into_result(res, &self.runtime) - } - - /// Create account for the receiver of the transaction. - pub fn create_account(mut self) -> Self { - self.transaction = self.transaction.create_account(); - self - } - - /// Deploy Wasm binary - pub fn deploy_contract(mut self, code: Vec) -> Self { - self.transaction = self.transaction.deploy_contract(code); - self - } - - /// Execute contract call to receiver - pub fn function_call( - mut self, - function_name: String, - args: Vec, - gas: Gas, - deposit: Balance, - ) -> Self { - self.transaction = self.transaction.function_call(function_name, args, gas, deposit); - self - } - - /// Transfer deposit to receiver - pub fn transfer(mut self, deposit: Balance) -> Self { - self.transaction = self.transaction.transfer(deposit); - self - } - - /// Express interest in becoming a validator - pub fn stake(mut self, stake: Balance, public_key: PublicKey) -> Self { - self.transaction = self.transaction.stake(stake, public_key); - self - } - - /// Add access key, either FunctionCall or FullAccess - pub fn add_key(mut self, public_key: PublicKey, access_key: AccessKey) -> Self { - self.transaction = self.transaction.add_key(public_key, access_key); - self - } - - /// Delete an access key - pub fn delete_key(mut self, public_key: PublicKey) -> Self { - self.transaction = self.transaction.delete_key(public_key); - self - } - - /// Delete an account and send remaining balance to `beneficiary_id` - pub fn delete_account(mut self, beneficiary_id: AccountId) -> Self { - self.transaction = self.transaction.delete_account(String::from(beneficiary_id)); - self - } -} - -/// A user that can sign transactions. It includes a signer and an account id. -pub struct UserAccount { - runtime: Rc>, - pub account_id: AccountId, - pub signer: InMemorySigner, -} - -impl Debug for UserAccount { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("UserAccount").field("account_id", &self.account_id).finish() - } -} - -impl UserAccount { - #[doc(hidden)] - pub fn new( - runtime: &Rc>, - account_id: AccountId, - signer: InMemorySigner, - ) -> Self { - let runtime = Rc::clone(runtime); - Self { runtime, account_id, signer } - } - - /// Returns a copy of the `account_id` - pub fn account_id(&self) -> AccountId { - self.account_id.clone() - } - /// Look up the account information on chain. - pub fn account(&self) -> Option { - (*self.runtime).borrow().view_account(self.account_id.as_str()) - } - /// Transfer yoctoNear to another account - pub fn transfer(&self, to: AccountId, deposit: Balance) -> ExecutionResult { - self.submit_transaction(self.transaction(to).transfer(deposit)) - } - - /// Make a contract call. `pending_tx` includes the receiver, the method to call as well as its arguments. - /// Note: You will most likely not be using this method directly but rather the [`call!`](./macro.call.html) macro. - pub fn function_call( - &self, - pending_tx: PendingContractTx, - gas: Gas, - deposit: Balance, - ) -> ExecutionResult { - self.call(pending_tx.receiver_id, &pending_tx.method, &pending_tx.args, gas, deposit) - } - - pub fn call( - &self, - receiver_id: AccountId, - method: &str, - args: &[u8], - gas: Gas, - deposit: Balance, - ) -> ExecutionResult { - self.submit_transaction(self.transaction(receiver_id).function_call( - method.to_string(), - args.into(), - gas, - deposit, - )) - } - - /// Deploy a contract and create its account for `account_id`. - /// Note: You will most likely not be using this method directly but rather the [`deploy!`](./macro.deploy.html) macro. - pub fn deploy( - &self, - wasm_bytes: &[u8], - account_id: AccountId, - deposit: Balance, - ) -> UserAccount { - let signer = - InMemorySigner::from_seed(account_id.as_str(), KeyType::ED25519, account_id.as_str()); - self.submit_transaction( - self.transaction(account_id.clone()) - .create_account() - .add_key(signer.public_key(), AccessKey::full_access()) - .transfer(deposit) - .deploy_contract(wasm_bytes.to_vec()), - ) - .assert_success(); - UserAccount::new(&self.runtime, account_id, signer) - } - - /// Deploy a contract and in the same transaction call its initialization method. - /// Note: You will most likely not be using this method directly but rather the [`deploy!`](./macro.deploy.html) macro. - pub fn deploy_and_initialize( - &self, - wasm_bytes: &[u8], - pending_tx: PendingContractTx, - deposit: Balance, - gas: Gas, - ) -> UserAccount { - self.deploy_and_init( - wasm_bytes, - pending_tx.receiver_id, - &pending_tx.method, - &pending_tx.args, - deposit, - gas, - ) - } - - pub fn deploy_and_init( - &self, - wasm_bytes: &[u8], - account_id: AccountId, - method: &str, - args: &[u8], - deposit: Balance, - gas: Gas, - ) -> UserAccount { - let signer = - InMemorySigner::from_seed(account_id.as_str(), KeyType::ED25519, account_id.as_str()); - self.submit_transaction( - self.transaction(account_id.clone()) - .create_account() - .add_key(signer.public_key(), AccessKey::full_access()) - .transfer(deposit) - .deploy_contract(wasm_bytes.to_vec()) - .function_call(method.to_string(), args.to_vec(), gas, 0), - ) - .assert_success(); - UserAccount::new(&self.runtime, account_id, signer) - } - - fn transaction(&self, receiver_id: AccountId) -> Transaction { - let nonce = (*self.runtime) - .borrow() - .view_access_key(self.account_id.as_str(), &self.signer.public_key()) - .unwrap() - .nonce - + 1; - Transaction::new( - String::from(self.account_id()), - self.signer.public_key(), - String::from(receiver_id), - nonce, - CryptoHash::default(), - ) - } - - /// Create a user transaction to `receiver_id` to be signed the current user - pub fn create_transaction(&self, receiver_id: AccountId) -> UserTransaction { - let transaction = self.transaction(receiver_id); - let runtime = Rc::clone(&self.runtime); - UserTransaction { transaction, signer: self.signer.clone(), runtime } - } - - fn submit_transaction(&self, transaction: Transaction) -> ExecutionResult { - let res = (*self.runtime).borrow_mut().resolve_tx(transaction.sign(&self.signer)).unwrap(); - (*self.runtime).borrow_mut().process_all().unwrap(); - outcome_into_result(res, &self.runtime) - } - - /// Call a view method on a contract. - /// Note: You will most likely not be using this method directly but rather the [`view!`](./macros.view.html) macro. - pub fn view_method_call(&self, pending_tx: PendingContractTx) -> ViewResult { - self.view(pending_tx.receiver_id, &pending_tx.method, &pending_tx.args) - } - - pub fn view(&self, receiver_id: AccountId, method: &str, args: &[u8]) -> ViewResult { - (*self.runtime).borrow().view_method_call(receiver_id.as_str(), method, args) - } - - /// Creates a user and is signed by the `signer_user` - pub fn create_user_from( - &self, - signer_user: &UserAccount, - account_id: AccountId, - amount: Balance, - ) -> UserAccount { - let signer = - InMemorySigner::from_seed(account_id.as_str(), KeyType::ED25519, account_id.as_str()); - signer_user - .submit_transaction( - signer_user - .transaction(account_id.clone()) - .create_account() - .add_key(signer.public_key(), AccessKey::full_access()) - .transfer(amount), - ) - .assert_success(); - UserAccount::new(&self.runtime, account_id, signer) - } - - /// Create a new user where the signer is this user account - pub fn create_user(&self, account_id: AccountId, amount: Balance) -> UserAccount { - self.create_user_from(self, account_id, amount) - } - - /// Returns a reference to a memory location of the standalone runtime. - /// - /// # Examples - /// ``` - /// let master_account = near_sdk_sim::init_simulator(None); - /// let runtime = master_account.borrow_runtime(); - /// - /// // with use - /// let _block = runtime.current_block(); - /// ``` - pub fn borrow_runtime(&self) -> Ref { - (*self.runtime).borrow() - } - - /// Returns a mutable memory location to the standalone runtime. - /// - /// # Examples - /// ``` - /// let master_account = near_sdk_sim::init_simulator(None); - /// let mut runtime = master_account.borrow_runtime_mut(); - /// - /// // with use - /// runtime.produce_block().unwrap(); - /// ``` - pub fn borrow_runtime_mut(&self) -> RefMut { - (*self.runtime).borrow_mut() - } -} - -/// A account for a contract that includes a reference to the contract proxy and a user account -pub struct ContractAccount { - pub user_account: UserAccount, - pub contract: T, -} - -// TODO: find smarter way to respond to all `self.user_account` methods for a ContractAccount -impl ContractAccount { - pub fn account_id(&self) -> AccountId { - self.user_account.account_id() - } - pub fn account(&self) -> Option { - self.user_account.account() - } -} - -/// The simulator takes an optional GenesisConfig, which sets up the fees and other settings. -/// It returns the `master_account` which can then create accounts and deploy contracts. -pub fn init_simulator(genesis_config: Option) -> UserAccount { - let (runtime, signer, root_account_id) = init_runtime(genesis_config); - UserAccount::new(&Rc::new(RefCell::new(runtime)), root_account_id, signer) -} - -/// Deploys a contract. Will either deploy or deploy and initialize a contract. -/// Returns a `ContractAccount` where `T` is the first argument. -/// -/// # Examples -/// -/// The simplest example is deploying a contract without initializing it. -/// -/// -/// This example deploys and initializes the contract. -/// -/// ``` -/// # lazy_static_include::lazy_static_include_bytes! { -/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", -/// # } -/// use near_sdk_sim::*; -/// use fungible_token::ContractContract; -/// use std::convert::TryInto; -/// use near_sdk::AccountId; -/// let master_account = near_sdk_sim::init_simulator(None); -/// let master_account_id: AccountId = master_account.account_id().try_into().unwrap(); -/// let initial_balance = near_sdk_sim::to_yocto("35"); -/// let contract = deploy! { -/// contract: ContractContract, -/// contract_id: "contract", -/// bytes: &TOKEN_WASM_BYTES, -/// signer_account: master_account, -/// init_method: new_default_meta(master_account_id, initial_balance.into()) -/// }; -/// ``` -/// This example used the default values for the initial deposit to the new contract's account and gas for the contract call. -/// So it is the same as: -/// ``` -/// # lazy_static_include::lazy_static_include_bytes! { -/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", -/// # } -/// use fungible_token::ContractContract; -/// use near_sdk_sim::*; -/// use near_sdk::AccountId; -/// let master_account = near_sdk_sim::init_simulator(None); -/// let master_account_id: AccountId = master_account.account_id(); -/// let initial_balance = near_sdk_sim::to_yocto("35"); -/// let contract = deploy! { -/// contract: ContractContract, -/// contract_id: "contract", -/// bytes: &TOKEN_WASM_BYTES, -/// signer_account: master_account, -/// deposit: near_sdk_sim::STORAGE_AMOUNT, // Deposit required to cover contract storage. -/// gas: near_sdk_sim::DEFAULT_GAS, -/// init_method: new_default_meta(master_account_id, initial_balance.into()), -/// }; -/// ``` -#[macro_export] -macro_rules! deploy { - ($contract: ident, $account_id:expr, $wasm_bytes: expr, $user:expr $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user, near_sdk_sim::STORAGE_AMOUNT) - }; - ($contract: ident, $account_id:expr, $wasm_bytes: expr, $user:expr, $deposit: expr $(,)?) => { - near_sdk_sim::ContractAccount { - user_account: $user.deploy($wasm_bytes, near_sdk::AccountId::new_unchecked($account_id.to_string()), $deposit), - contract: $contract { account_id: near_sdk::AccountId::new_unchecked($account_id.to_string()) }, - } - }; - ($contract: ident, $account_id:expr, $wasm_bytes: expr, $user_id:expr, $deposit:expr, $gas:expr, $method: ident, $($arg:expr),* $(,)?) => { - { - let __contract = $contract { account_id: near_sdk::AccountId::new_unchecked($account_id.to_string()) }; - near_sdk_sim::ContractAccount { - user_account: $user_id.deploy_and_initialize($wasm_bytes, __contract.$method($($arg),*), $deposit, $gas), - contract: __contract, - } - } - }; - (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user) - }; - (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, deposit: $deposit: expr $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user, $deposit) - }; - (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, deposit: $deposit: expr, gas: $gas:expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user, $deposit, $gas, $method, $($arg),*) - }; - (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, gas: $gas:expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user, near_sdk_sim::STORAGE_AMOUNT, $gas, $method, $($arg),*) - }; - (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, deposit: $deposit: expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user, $deposit, near_sdk_sim::DEFAULT_GAS, $method, $($arg),*) - }; - (contract: $contract: ident, contract_id: $account_id:expr, bytes: $wasm_bytes: expr, signer_account: $user:expr, init_method: $method: ident($($arg:expr),*) $(,)?) => { - deploy!($contract, $account_id, $wasm_bytes, $user, near_sdk_sim::STORAGE_AMOUNT, near_sdk_sim::DEFAULT_GAS, $method, $($arg),*) - }; -} - -/// Makes a contract call to a [`ContractAccount`](./struct.ContractAccount.html) returning a [`ExecutionResult`](./struct.ExecutionResult.html). -/// -/// -/// # Examples: -/// -/// ``` -/// # lazy_static_include::lazy_static_include_bytes! { -/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", -/// # } -/// # use near_sdk_sim::*; -/// # use fungible_token::ContractContract; -/// # use std::convert::TryInto; -/// # use near_sdk::AccountId; -/// # let master_account = near_sdk_sim::init_simulator(None); -/// # let master_account_id: AccountId = master_account.account_id().try_into().unwrap(); -/// # let initial_balance = near_sdk_sim::to_yocto("35"); -/// # let contract = deploy! { -/// # contract: ContractContract, -/// # contract_id: "contract", -/// # bytes: &TOKEN_WASM_BYTES, -/// # signer_account: master_account, -/// # deposit: near_sdk_sim::STORAGE_AMOUNT, // Deposit required to cover contract storage. -/// # gas: near_sdk_sim::DEFAULT_GAS, -/// # init_method: new_default_meta(master_account_id.clone(), initial_balance.into()) -/// # }; -/// use near_sdk_sim::to_yocto; -/// // Uses default values for gas and deposit. -/// let res = call!( -/// master_account, -/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None) -/// ); -/// // Equivalent to -/// let res = call!( -/// master_account, -/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None), -/// 0, -/// near_sdk_sim::DEFAULT_GAS -/// ); -/// // Can also specify either deposit or gas -/// let res = call!( -/// master_account, -/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None), -/// deposit = 0 -/// ); -/// let res = call!( -/// master_account, -/// contract.ft_transfer(master_account_id.clone(), to_yocto("100").into(), None), -/// gas = near_sdk_sim::DEFAULT_GAS -/// ); -/// ``` -#[macro_export] -macro_rules! call { - ($signer:expr, $deposit: expr, $gas: expr, $contract: ident, $method:ident, $($arg:expr),*) => { - $signer.function_call((&$contract).contract.$method($($arg),*), $gas, $deposit) - }; - ($signer:expr, $contract: ident.$method:ident($($arg:expr),*), $deposit: expr, $gas: expr) => { - call!($signer, $deposit, $gas, $contract, $method, $($arg),*) - }; - ($signer:expr, $contract: ident.$method:ident($($arg:expr),*)) => { - call!($signer, 0, near_sdk_sim::DEFAULT_GAS, $contract, $method, $($arg),*) - }; - ($signer:expr, $contract: ident.$method:ident($($arg:expr),*), gas=$gas_or_deposit: expr) => { - call!($signer, 0, $gas_or_deposit, $contract, $method, $($arg),*) - }; - ($signer:expr, $contract: ident.$method:ident($($arg:expr),*), deposit=$gas_or_deposit: expr) => { - call!($signer, $gas_or_deposit, near_sdk_sim::DEFAULT_GAS, $contract, $method, $($arg),*) - }; -} - -/// Calls a view method on the contract account. -/// -/// Example: -/// ``` -/// # lazy_static_include::lazy_static_include_bytes! { -/// # TOKEN_WASM_BYTES => "../examples/fungible-token/res/fungible_token.wasm", -/// # } -/// # use near_sdk_sim::*; -/// # use fungible_token::ContractContract; -/// # use std::convert::TryInto; -/// # use near_sdk::AccountId; -/// # let master_account = near_sdk_sim::init_simulator(None); -/// # let master_account_id: AccountId = master_account.account_id().try_into().unwrap(); -/// # let initial_balance = near_sdk_sim::to_yocto("35"); -/// # let contract = deploy! { -/// # contract: ContractContract, -/// # contract_id: "contract", -/// # bytes: &TOKEN_WASM_BYTES, -/// # signer_account: master_account, -/// # deposit: near_sdk_sim::STORAGE_AMOUNT, // Deposit required to cover contract storage. -/// # gas: near_sdk_sim::DEFAULT_GAS, -/// # init_method: new_default_meta(master_account_id.clone(), initial_balance.into()) -/// # }; -/// let res = view!(contract.ft_balance_of(master_account_id)); -/// ``` -/// -#[macro_export] -macro_rules! view { - ($contract: ident.$method:ident($($arg:expr),*)) => { - (&$contract).user_account.view_method_call((&$contract).contract.$method($($arg),*)) - }; -} From 98aeceb04fd57e1c1da49be1ca7415fe9acbc199 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 26 Sep 2023 19:18:32 +0200 Subject: [PATCH 09/35] update CHANGELOG.md --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index de465d806..8ed750fbc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Added +- **BREAKING** Added [NEP-199 - Non-Fungible Token Royalties and Payouts](https://github.com/willemneal/NEPs/blob/18828873648eff1a2e8464db234aefd70918b3e0/neps/nep-0199.md) implementation. This +will need a state migration for any contracts using NFT of previous versions due to an extra field. + ### Fixed - Exposed missing iterator types used in `near_sdk::store::UnorderedSet`. [PR 961](https://github.com/near/near-sdk-rs/pull/961) From 3d7a4ad7f0ff5d1c5ba36ae779abf283e2a8c513 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 26 Sep 2023 19:55:57 +0200 Subject: [PATCH 10/35] fix test --- examples/non-fungible-token/tests/workspaces/test_core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/non-fungible-token/tests/workspaces/test_core.rs b/examples/non-fungible-token/tests/workspaces/test_core.rs index a7fafa85b..47195351c 100644 --- a/examples/non-fungible-token/tests/workspaces/test_core.rs +++ b/examples/non-fungible-token/tests/workspaces/test_core.rs @@ -185,7 +185,7 @@ async fn simulate_transfer_call_receiver_panics_and_nft_resolve_transfer_produce Some("transfer & call"), "incorrect message", )) - .gas(30_000_000_000_000) + .gas(1) .deposit(ONE_YOCTO) .transact() .await?; From 562b284e526f9fb179c2f2ecebbe47b24e5ca298 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 26 Sep 2023 20:40:34 +0200 Subject: [PATCH 11/35] fixes and tests --- examples/non-fungible-token/nft/src/lib.rs | 24 ++++---- .../tests/workspaces/main.rs | 1 + .../tests/workspaces/test_approval.rs | 16 ++--- .../tests/workspaces/test_enumeration.rs | 8 +-- .../tests/workspaces/test_payout.rs | 59 +++++++++++++++++++ .../src/non_fungible_token/payout/mod.rs | 16 ++--- 6 files changed, 88 insertions(+), 36 deletions(-) create mode 100644 examples/non-fungible-token/tests/workspaces/test_payout.rs diff --git a/examples/non-fungible-token/nft/src/lib.rs b/examples/non-fungible-token/nft/src/lib.rs index bc319f674..14de726b4 100644 --- a/examples/non-fungible-token/nft/src/lib.rs +++ b/examples/non-fungible-token/nft/src/lib.rs @@ -30,8 +30,7 @@ use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; use near_sdk::collections::LazyOption; use near_sdk::json_types::U128; use near_sdk::{ - assert_one_yocto, env, near_bindgen, require, AccountId, BorshStorageKey, PanicOnDefault, - Promise, PromiseOrValue, + env, near_bindgen, require, AccountId, BorshStorageKey, PanicOnDefault, Promise, PromiseOrValue, }; use std::collections::HashMap; @@ -216,15 +215,14 @@ impl NonFungibleTokenEnumeration for Contract { } } +#[near_bindgen] impl NonFungibleTokenPayout for Contract { + #[allow(unused_variables)] fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option) -> Payout { - let owner_id = self.tokens.owner_by_id.get(&token_id).expect("No such token_id"); - self.tokens - .royalties - .as_ref() - .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) + self.tokens.nft_payout(token_id, balance, max_len_payout) } + #[payable] fn nft_transfer_payout( &mut self, receiver_id: AccountId, @@ -234,10 +232,14 @@ impl NonFungibleTokenPayout for Contract { balance: U128, max_len_payout: Option, ) -> Payout { - assert_one_yocto(); - let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); - self.nft_transfer(receiver_id, token_id, approval_id, memo); - payout + self.tokens.nft_transfer_payout( + receiver_id, + token_id, + approval_id, + memo, + balance, + max_len_payout, + ) } } diff --git a/examples/non-fungible-token/tests/workspaces/main.rs b/examples/non-fungible-token/tests/workspaces/main.rs index b0d04236c..9a09d5bc0 100644 --- a/examples/non-fungible-token/tests/workspaces/main.rs +++ b/examples/non-fungible-token/tests/workspaces/main.rs @@ -1,4 +1,5 @@ mod test_approval; mod test_core; mod test_enumeration; +mod test_payout; mod utils; diff --git a/examples/non-fungible-token/tests/workspaces/test_approval.rs b/examples/non-fungible-token/tests/workspaces/test_approval.rs index eb26802c8..e8121057a 100644 --- a/examples/non-fungible-token/tests/workspaces/test_approval.rs +++ b/examples/non-fungible-token/tests/workspaces/test_approval.rs @@ -50,12 +50,8 @@ async fn simulate_simple_approve() -> anyhow::Result<()> { assert!(!alice_approval_id_is_2); // alternatively, one could check the data returned by nft_token - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; let mut expected_approvals: HashMap = HashMap::new(); expected_approvals.insert(AccountId::try_from(alice.id().to_string())?, 1); assert_eq!(token.approved_account_ids.unwrap(), expected_approvals); @@ -157,12 +153,8 @@ async fn simulate_approved_account_transfers_token() -> anyhow::Result<()> { assert!(res.is_success()); // token now owned by alice - let token = nft_contract - .call("nft_token") - .args_json((TOKEN_ID,)) - .view() - .await? - .json::()?; + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; assert_eq!(token.owner_id.to_string(), alice.id().to_string()); Ok(()) diff --git a/examples/non-fungible-token/tests/workspaces/test_enumeration.rs b/examples/non-fungible-token/tests/workspaces/test_enumeration.rs index 330ab4b16..f4ca13261 100644 --- a/examples/non-fungible-token/tests/workspaces/test_enumeration.rs +++ b/examples/non-fungible-token/tests/workspaces/test_enumeration.rs @@ -97,12 +97,8 @@ async fn simulate_enum_nft_supply_for_owner() -> anyhow::Result<()> { let (nft_contract, alice, _, _) = init(&worker).await?; // Get number from account with no NFTs - let owner_num_tokens: U128 = nft_contract - .call("nft_supply_for_owner") - .args_json((alice.id(),)) - .view() - .await? - .json()?; + let owner_num_tokens: U128 = + nft_contract.call("nft_supply_for_owner").args_json((alice.id(),)).view().await?.json()?; assert_eq!(owner_num_tokens, U128::from(0)); let owner_num_tokens: U128 = nft_contract diff --git a/examples/non-fungible-token/tests/workspaces/test_payout.rs b/examples/non-fungible-token/tests/workspaces/test_payout.rs new file mode 100644 index 000000000..d38928890 --- /dev/null +++ b/examples/non-fungible-token/tests/workspaces/test_payout.rs @@ -0,0 +1,59 @@ +use crate::utils::{init, TOKEN_ID}; +use near_contract_standards::non_fungible_token::Token; +use near_sdk::json_types::U128; +use near_sdk::ONE_YOCTO; + +#[tokio::test] +async fn simulate_payout() -> anyhow::Result<()> { + let worker = workspaces::sandbox().await?; + let (nft_contract, alice, _, _) = init(&worker).await?; + + let res = nft_contract + .call("nft_payout") + .args_json((TOKEN_ID, U128::from(1), Option::::None)) + .max_gas() + .transact() + .await?; + assert!(res.is_success()); + + // A single NFT transfer event should have been logged: + assert_eq!(res.logs().len(), 0); + + Ok(()) +} + +#[tokio::test] +async fn nft_transfer_payout() -> anyhow::Result<()> { + let worker = workspaces::sandbox().await?; + let (nft_contract, alice, _, _) = init(&worker).await?; + + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + assert_eq!(token.owner_id.to_string(), nft_contract.id().to_string()); + + let res = nft_contract + .call("nft_transfer_payout") + .args_json(( + alice.id(), + TOKEN_ID, + Option::::None, + Some("simple transfer".to_string()), + U128::from(1), + Option::::None, + )) + .max_gas() + .deposit(ONE_YOCTO) + .transact() + .await?; + + assert!(res.is_success()); + + // A single NFT transfer event should have been logged: + assert_eq!(res.logs().len(), 1); + + let token = + nft_contract.call("nft_token").args_json((TOKEN_ID,)).view().await?.json::()?; + assert_eq!(token.owner_id.to_string(), alice.id().to_string()); + + Ok(()) +} diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 10ef32e22..7e0592dfb 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -65,9 +65,7 @@ pub struct Royalties { /// balance: U128, /// max_len_payout: Option, /// ) -> Payout { -/// let owner_id = self.tokens.owner_by_id.get(&token_id).expect("No such token_id"); -/// self.tokens.royalties.as_ref() -/// .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) +/// self.tokens.nft_payout(token_id, balance, max_len_payout) /// } /// #[payable] /// fn nft_transfer_payout( @@ -79,10 +77,14 @@ pub struct Royalties { /// balance: U128, /// max_len_payout: Option, /// ) -> Payout { -/// assert_one_yocto(); -/// let payout = self.nft_payout(token_id.clone(), balance, max_len_payout); -/// self.nft_transfer(receiver_id, token_id, approval_id, memo); -/// payout +/// self.tokens.nft_transfer_payout( +/// receiver_id, +/// token_id, +/// approval_id, +/// memo, +/// balance, +/// max_len_payout, +/// ) /// } /// } /// ``` From 933c02924c9f3313688943c00f5a2777d9bebaba Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 26 Sep 2023 20:56:45 +0200 Subject: [PATCH 12/35] more tests --- examples/non-fungible-token/nft/src/lib.rs | 78 ++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/examples/non-fungible-token/nft/src/lib.rs b/examples/non-fungible-token/nft/src/lib.rs index 14de726b4..d929e8aa2 100644 --- a/examples/non-fungible-token/nft/src/lib.rs +++ b/examples/non-fungible-token/nft/src/lib.rs @@ -360,6 +360,84 @@ mod tests { } } + #[test] + fn test_transfer_payout() { + let mut context = get_context(accounts(0)); + testing_env!(context.build()); + let mut contract = Contract::new_default_meta(accounts(0).into()); + + testing_env!(context + .storage_usage(env::storage_usage()) + .attached_deposit(MINT_STORAGE_COST) + .predecessor_account_id(accounts(0)) + .build()); + let token_id = "0".to_string(); + contract.nft_mint(token_id.clone(), accounts(0), sample_token_metadata()); + + testing_env!(context + .storage_usage(env::storage_usage()) + .attached_deposit(1) + .predecessor_account_id(accounts(0)) + .build()); + + let payout = contract.nft_transfer_payout( + accounts(1), + token_id.clone(), + None, + None, + U128::from(1), + None, + ); + + let mut expected_payout = HashMap::new(); + expected_payout.insert(accounts(0), U128::from(1)); + + assert_eq!(payout.payout, expected_payout); + + testing_env!(context + .storage_usage(env::storage_usage()) + .account_balance(env::account_balance()) + .is_view(true) + .attached_deposit(0) + .build()); + + if let Some(token) = contract.nft_token(token_id.clone()) { + assert_eq!(token.token_id, token_id); + assert_eq!(token.owner_id, accounts(1)); + assert_eq!(token.metadata.unwrap(), sample_token_metadata()); + assert_eq!(token.approved_account_ids.unwrap(), HashMap::new()); + } else { + panic!("token not correctly created, or not found by nft_token"); + } + } + + #[test] + fn test_payout() { + let mut context = get_context(accounts(0)); + testing_env!(context.build()); + let mut contract = Contract::new_default_meta(accounts(0).into()); + + testing_env!(context + .storage_usage(env::storage_usage()) + .attached_deposit(MINT_STORAGE_COST) + .predecessor_account_id(accounts(0)) + .build()); + let token_id = "0".to_string(); + contract.nft_mint(token_id.clone(), accounts(0), sample_token_metadata()); + + testing_env!(context + .storage_usage(env::storage_usage()) + .predecessor_account_id(accounts(0)) + .build()); + + let payout = contract.nft_payout(token_id, U128::from(1), None); + + let mut expected_payout = HashMap::new(); + expected_payout.insert(accounts(0), U128::from(1)); + + assert_eq!(payout.payout, expected_payout); + } + #[test] fn test_approve() { let mut context = get_context(accounts(0)); From 2f2779d1bf7f3bafaa3c43b548e0babf6496f3bd Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 26 Sep 2023 21:01:26 +0200 Subject: [PATCH 13/35] better comment --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8ed750fbc..8056cbecc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ ### Added - **BREAKING** Added [NEP-199 - Non-Fungible Token Royalties and Payouts](https://github.com/willemneal/NEPs/blob/18828873648eff1a2e8464db234aefd70918b3e0/neps/nep-0199.md) implementation. This -will need a state migration for any contracts using NFT of previous versions due to an extra field. +will need a state migration in case of upgrade for any contracts using NFT of previous versions due to an extra field. ### Fixed - Exposed missing iterator types used in `near_sdk::store::UnorderedSet`. [PR 961](https://github.com/near/near-sdk-rs/pull/961) From 6f07b529ce0391aea2c1e4d18ddc8b0c56aef17b Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 29 Sep 2023 17:31:18 +0200 Subject: [PATCH 14/35] return the missing check --- examples/non-fungible-token/tests/workspaces/test_core.rs | 2 +- .../src/non_fungible_token/core/core_impl.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/examples/non-fungible-token/tests/workspaces/test_core.rs b/examples/non-fungible-token/tests/workspaces/test_core.rs index 47195351c..a7fafa85b 100644 --- a/examples/non-fungible-token/tests/workspaces/test_core.rs +++ b/examples/non-fungible-token/tests/workspaces/test_core.rs @@ -185,7 +185,7 @@ async fn simulate_transfer_call_receiver_panics_and_nft_resolve_transfer_produce Some("transfer & call"), "incorrect message", )) - .gas(1) + .gas(30_000_000_000_000) .deposit(ONE_YOCTO) .transact() .await?; diff --git a/near-contract-standards/src/non_fungible_token/core/core_impl.rs b/near-contract-standards/src/non_fungible_token/core/core_impl.rs index 00d7d4e88..42f80968a 100644 --- a/near-contract-standards/src/non_fungible_token/core/core_impl.rs +++ b/near-contract-standards/src/non_fungible_token/core/core_impl.rs @@ -396,6 +396,7 @@ impl NonFungibleTokenCore for NonFungibleToken { msg: String, ) -> PromiseOrValue { assert_one_yocto(); + require!(env::prepaid_gas() > GAS_FOR_NFT_TRANSFER_CALL, "More gas is required"); let sender_id = env::predecessor_account_id(); let (old_owner, old_approvals) = self.internal_transfer(&sender_id, &receiver_id, &token_id, approval_id, memo); From e4da3424ecc14cd3a3877c7abb04f34c385608aa Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sun, 1 Oct 2023 10:55:30 +0200 Subject: [PATCH 15/35] fix ci --- .github/workflows/test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6b46e6f1f..e32c5bfdd 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,10 +23,11 @@ jobs: toolchain: ${{ matrix.toolchain }} default: true target: wasm32-unknown-unknown - - name: downgrade `anstyle`,`clap`,`clap_lex` crates to support older Rust toolchain + - name: downgrade `anstyle`,`anstyle-parse`,`clap`,`clap_lex` crates to support older Rust toolchain if: matrix.toolchain == '1.69.0' run: | cargo update -p anstyle --precise 1.0.2 + cargo update -p anstyle-parse --precise 0.2.1 cargo update -p clap --precise 4.3.24 cargo update -p clap_lex --precise 0.5.0 - uses: Swatinem/rust-cache@v1 From d76ff5d9e146e6012b34a833905f58b4ca2c35a0 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sat, 7 Oct 2023 19:42:36 +0200 Subject: [PATCH 16/35] fix review comments --- near-contract-standards/src/non_fungible_token/payout/mod.rs | 5 +++++ .../src/non_fungible_token/payout/payout_impl.rs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 7e0592dfb..f3091b4a6 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -8,17 +8,22 @@ use std::collections::HashMap; type BasisPoint = u16; +/// This struct represents NFT royalty payout for each receiver. #[derive(Default, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] #[serde(crate = "near_sdk::serde")] pub struct Payout { pub payout: HashMap, } +/// This struct represents percentage of total royalty per receiver as well as the total percentage +/// of distributed royalties based on incoming payment. #[derive(Deserialize, Serialize, BorshDeserialize, BorshSerialize, Default, Debug)] #[serde(crate = "near_sdk::serde")] pub struct Royalties { key_prefix: Vec, + /// A mapping of accounts to the percentage of total royalty to be distributed. pub accounts: HashMap, + /// Total percent of incoming balance used for royalties. pub percent: BasisPoint, } diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 98e4d431c..719481287 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -2,7 +2,7 @@ use super::NonFungibleTokenPayout; use crate::non_fungible_token::core::NonFungibleTokenCore; use crate::non_fungible_token::payout::*; use crate::non_fungible_token::NonFungibleToken; -use near_sdk::assert_one_yocto; +use near_sdk::{assert_one_yocto, env}; use near_sdk::{require, AccountId, Balance, IntoStorageKey}; use std::collections::HashMap; impl Royalties { @@ -48,7 +48,7 @@ impl Royalties { } fn apply_percent(percent: BasisPoint, int: u128) -> u128 { - int * percent as u128 / 100u128 + int.checked_mul(percent as u128).unwrap_or_else(|| env::panic_str("royalty overflow")) / 100u128 } impl NonFungibleTokenPayout for NonFungibleToken { From f3b3594f46efa30b791266df0808594905920fad Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sat, 7 Oct 2023 20:02:48 +0200 Subject: [PATCH 17/35] use TreeMap for rolayties storage --- near-contract-standards/src/non_fungible_token/payout/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index f3091b4a6..6b7a9adbf 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -1,6 +1,7 @@ mod payout_impl; use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; use near_sdk::AccountId; use serde::{Deserialize, Serialize}; @@ -22,7 +23,7 @@ pub struct Payout { pub struct Royalties { key_prefix: Vec, /// A mapping of accounts to the percentage of total royalty to be distributed. - pub accounts: HashMap, + pub accounts: TreeMap, /// Total percent of incoming balance used for royalties. pub percent: BasisPoint, } From 815a9eaf8a3a19e61cd75f0536f871e2dd5881aa Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 13 Oct 2023 18:18:38 +0200 Subject: [PATCH 18/35] apply review suggestions --- .../src/non_fungible_token/payout/mod.rs | 4 +-- .../non_fungible_token/payout/payout_impl.rs | 28 +++++++++++++------ 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 6b7a9adbf..de372e413 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -18,10 +18,8 @@ pub struct Payout { /// This struct represents percentage of total royalty per receiver as well as the total percentage /// of distributed royalties based on incoming payment. -#[derive(Deserialize, Serialize, BorshDeserialize, BorshSerialize, Default, Debug)] -#[serde(crate = "near_sdk::serde")] +#[derive(BorshDeserialize, BorshSerialize, Debug)] pub struct Royalties { - key_prefix: Vec, /// A mapping of accounts to the percentage of total royalty to be distributed. pub accounts: TreeMap, /// Total percent of incoming balance used for royalties. diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 719481287..aeaf3b770 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -4,18 +4,18 @@ use crate::non_fungible_token::payout::*; use crate::non_fungible_token::NonFungibleToken; use near_sdk::{assert_one_yocto, env}; use near_sdk::{require, AccountId, Balance, IntoStorageKey}; -use std::collections::HashMap; + impl Royalties { pub fn new(key_prefix: S) -> Self where S: IntoStorageKey, { - let temp_accounts: HashMap = HashMap::new(); - let this = - Self { key_prefix: key_prefix.into_storage_key(), accounts: temp_accounts, percent: 0 }; + let temp_accounts: TreeMap = TreeMap::new(key_prefix); + let this = Self { accounts: temp_accounts, percent: 0 }; this.validate(); this } + pub(crate) fn validate(&self) { require!(self.percent <= 100, "royalty percent must be between 0 - 100"); require!( @@ -24,11 +24,12 @@ impl Royalties { ); let mut total: BasisPoint = 0; self.accounts.iter().for_each(|(_, percent)| { - require!(*percent <= 100, "each royalty should be less than 100"); + require!(percent <= 100, "each royalty should be less than 100"); total += percent; }); require!(total <= 100, "total percent of each royalty split must be less than 100") } + pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { let royalty_payment = apply_percent(self.percent, balance); let mut payout = Payout { @@ -36,7 +37,7 @@ impl Royalties { .accounts .iter() .map(|(account, percent)| { - (account.clone(), apply_percent(*percent, royalty_payment).into()) + (account.clone(), apply_percent(percent, royalty_payment).into()) }) .collect(), }; @@ -52,10 +53,21 @@ fn apply_percent(percent: BasisPoint, int: u128) -> u128 { } impl NonFungibleTokenPayout for NonFungibleToken { - #[allow(unused_variables)] fn nft_payout(&self, token_id: String, balance: U128, max_len_payout: Option) -> Payout { let owner_id = self.owner_by_id.get(&token_id).expect("No such token_id"); - self.royalties.as_ref().map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)) + let payout = self + .royalties + .as_ref() + .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)); + + if let Some(max_len_payout) = max_len_payout { + require!( + payout.payout.len() <= max_len_payout as usize, + "payout number can't exceed `max_len_payout`" + ); + } + + payout } fn nft_transfer_payout( From c2e990e1a07cb93bb89fcbd4fd31eaf446a68ec4 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 13 Oct 2023 18:51:47 +0200 Subject: [PATCH 19/35] fixes --- near-contract-standards/src/non_fungible_token/payout/mod.rs | 2 ++ .../src/non_fungible_token/payout/payout_impl.rs | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index de372e413..b00ae4bc6 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -12,6 +12,7 @@ type BasisPoint = u16; /// This struct represents NFT royalty payout for each receiver. #[derive(Default, BorshDeserialize, BorshSerialize, Serialize, Deserialize)] #[serde(crate = "near_sdk::serde")] +#[borsh(crate = "near_sdk::borsh")] pub struct Payout { pub payout: HashMap, } @@ -19,6 +20,7 @@ pub struct Payout { /// This struct represents percentage of total royalty per receiver as well as the total percentage /// of distributed royalties based on incoming payment. #[derive(BorshDeserialize, BorshSerialize, Debug)] +#[borsh(crate = "near_sdk::borsh")] pub struct Royalties { /// A mapping of accounts to the percentage of total royalty to be distributed. pub accounts: TreeMap, diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index aeaf3b770..6832ee179 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -24,10 +24,10 @@ impl Royalties { ); let mut total: BasisPoint = 0; self.accounts.iter().for_each(|(_, percent)| { - require!(percent <= 100, "each royalty should be less than 100"); + require!(percent <= 100, "each royalty should be at most 100"); total += percent; }); - require!(total <= 100, "total percent of each royalty split must be less than 100") + require!(total <= 100, "total percent of each royalty split must be at most 100") } pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { From c3d8b67d9fb32190072f768a2995c956345d12b9 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sat, 14 Oct 2023 00:03:27 +0200 Subject: [PATCH 20/35] fixes --- examples/non-fungible-token/nft/src/lib.rs | 4 +--- near-contract-standards/src/non_fungible_token/payout/mod.rs | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/examples/non-fungible-token/nft/src/lib.rs b/examples/non-fungible-token/nft/src/lib.rs index 142988257..d417843cd 100644 --- a/examples/non-fungible-token/nft/src/lib.rs +++ b/examples/non-fungible-token/nft/src/lib.rs @@ -15,6 +15,7 @@ NOTES: - To prevent the deployed contract from being modified or deleted, it should not have any access keys on its account. */ + use near_contract_standards::non_fungible_token::approval::NonFungibleTokenApproval; use near_contract_standards::non_fungible_token::core::{ NonFungibleTokenCore, NonFungibleTokenResolver, @@ -26,9 +27,6 @@ use near_contract_standards::non_fungible_token::metadata::{ use near_contract_standards::non_fungible_token::payout::Payout; use near_contract_standards::non_fungible_token::{NonFungibleToken, NonFungibleTokenPayout}; use near_contract_standards::non_fungible_token::{Token, TokenId}; -use near_contract_standards::non_fungible_token::approval::NonFungibleTokenApproval; -use near_contract_standards::non_fungible_token::core::{NonFungibleTokenCore, NonFungibleTokenResolver}; -use near_contract_standards::non_fungible_token::enumeration::NonFungibleTokenEnumeration; use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use near_sdk::collections::LazyOption; use near_sdk::json_types::U128; diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index b00ae4bc6..731a7ae5c 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -1,6 +1,6 @@ mod payout_impl; -use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; use near_sdk::AccountId; From ee59a726f988666475ac6b9be2ab17e8d587046e Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sat, 14 Oct 2023 13:00:39 +0200 Subject: [PATCH 21/35] fixes --- near-contract-standards/src/non_fungible_token/payout/mod.rs | 3 ++- near-sdk/compilation_tests/borsh_storage_key_generics.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 731a7ae5c..fd38daa75 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -36,13 +36,14 @@ pub struct Royalties { /// # Examples /// /// ``` -/// use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +/// use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; /// use near_sdk::{PanicOnDefault, AccountId, PromiseOrValue, near_bindgen, assert_one_yocto}; /// use near_contract_standards::non_fungible_token::{core::NonFungibleTokenCore, NonFungibleToken, NonFungibleTokenPayout, payout::Payout, TokenId, Token}; /// use near_sdk::json_types::U128; /// /// #[near_bindgen] /// #[derive(BorshDeserialize, BorshSerialize, PanicOnDefault)] +/// #[borsh(crate = "near_sdk::borsh")] /// pub struct Contract { /// tokens: NonFungibleToken, ///} diff --git a/near-sdk/compilation_tests/borsh_storage_key_generics.rs b/near-sdk/compilation_tests/borsh_storage_key_generics.rs index e7a9c311c..b058601a1 100644 --- a/near-sdk/compilation_tests/borsh_storage_key_generics.rs +++ b/near-sdk/compilation_tests/borsh_storage_key_generics.rs @@ -1,6 +1,6 @@ //! Testing BorshStorageKey macro with lifetimes and generics. -use near_sdk::borsh::{self, BorshDeserialize, BorshSerialize}; +use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use near_sdk::collections::LookupMap; use near_sdk::{near_bindgen, BorshStorageKey}; From 5ec54b33c86fbc58416b31eb8a4ec1b74d503643 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sat, 14 Oct 2023 13:15:29 +0200 Subject: [PATCH 22/35] fix --- examples/non-fungible-token/tests/workspaces/test_payout.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/non-fungible-token/tests/workspaces/test_payout.rs b/examples/non-fungible-token/tests/workspaces/test_payout.rs index d38928890..d73e04abf 100644 --- a/examples/non-fungible-token/tests/workspaces/test_payout.rs +++ b/examples/non-fungible-token/tests/workspaces/test_payout.rs @@ -5,7 +5,7 @@ use near_sdk::ONE_YOCTO; #[tokio::test] async fn simulate_payout() -> anyhow::Result<()> { - let worker = workspaces::sandbox().await?; + let worker = near_workspaces::sandbox().await?; let (nft_contract, alice, _, _) = init(&worker).await?; let res = nft_contract @@ -24,7 +24,7 @@ async fn simulate_payout() -> anyhow::Result<()> { #[tokio::test] async fn nft_transfer_payout() -> anyhow::Result<()> { - let worker = workspaces::sandbox().await?; + let worker = near_workspaces::sandbox().await?; let (nft_contract, alice, _, _) = init(&worker).await?; let token = From b7a0a227b1e59071a0de3b81f69b812f785a2caf Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Mon, 23 Oct 2023 00:55:11 +0200 Subject: [PATCH 23/35] add tests --- .../src/non_fungible_token/core/core_impl.rs | 5 +- .../non_fungible_token/payout/payout_impl.rs | 150 +++++++++++++++++- 2 files changed, 152 insertions(+), 3 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/core/core_impl.rs b/near-contract-standards/src/non_fungible_token/core/core_impl.rs index 4372bb43c..6951ca5d0 100644 --- a/near-contract-standards/src/non_fungible_token/core/core_impl.rs +++ b/near-contract-standards/src/non_fungible_token/core/core_impl.rs @@ -92,7 +92,10 @@ impl NonFungibleToken { tokens_per_owner: enumeration_prefix.map(LookupMap::new), approvals_by_id, next_approval_id_by_id, - royalties: royalties_prefix.map(Royalties::new), + royalties: royalties_prefix.map(|prefix| { + // TODO: Figure out where the percent has to be coming from. + Royalties::new(prefix, 0) + }), }; this.measure_min_token_storage_cost(); this diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 6832ee179..9003d86b0 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -6,12 +6,12 @@ use near_sdk::{assert_one_yocto, env}; use near_sdk::{require, AccountId, Balance, IntoStorageKey}; impl Royalties { - pub fn new(key_prefix: S) -> Self + pub fn new(key_prefix: S, percent: BasisPoint) -> Self where S: IntoStorageKey, { let temp_accounts: TreeMap = TreeMap::new(key_prefix); - let this = Self { accounts: temp_accounts, percent: 0 }; + let this = Self { accounts: temp_accounts, percent }; this.validate(); this } @@ -48,6 +48,7 @@ impl Royalties { } } +// TODO: Perhaps redo this function so that it never overflows. fn apply_percent(percent: BasisPoint, int: u128) -> u128 { int.checked_mul(percent as u128).unwrap_or_else(|| env::panic_str("royalty overflow")) / 100u128 } @@ -85,3 +86,148 @@ impl NonFungibleTokenPayout for NonFungibleToken { payout } } +#[cfg(test)] +mod tests { + use crate::non_fungible_token::payout::payout_impl::apply_percent; + use crate::non_fungible_token::payout::Royalties; + use near_sdk::collections::TreeMap; + use near_sdk::json_types::U128; + use near_sdk::{AccountId, Balance}; + use std::mem; + + const KEY_PREFIX: &[u8] = "test_prefix".as_bytes(); + + #[test] + fn validate_happy_path() { + let mut map = TreeMap::new(KEY_PREFIX); + + // Works with up to 100% and at most 10 accounts. + for idx in 0..10 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + } + + let mut royalties = Royalties::new(KEY_PREFIX, 100); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + + // Make sure that max royalties works. + let owner_id = AccountId::new_unchecked("alice".to_string()); + let payout = royalties.create_payout(Balance::MAX / 100, &owner_id); + for (key, value) in payout.payout.iter() { + map.contains_key(key); + if *key == owner_id { + assert_eq!(*value, U128::from(0)); + } else { + assert_eq!(*value, U128::from(apply_percent(10, Balance::MAX / 100))); + } + } + } + + #[test] + fn validate_owner_rest_path() { + let mut map = TreeMap::new(KEY_PREFIX); + + for idx in 0..10 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + } + + let mut royalties = Royalties::new(KEY_PREFIX, 80); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + + // Make sure we don't overflow and don't end up with mismatched results due to using int as + // opposed to float. + let balance = Balance::MAX / 100_00 * 100; + let owner_id = AccountId::new_unchecked("alice".to_string()); + let payout = royalties.create_payout(balance, &owner_id); + for (key, value) in payout.payout.iter() { + map.contains_key(key); + if *key == owner_id { + assert_eq!(*value, U128::from(apply_percent(20, balance))); + } else { + assert_eq!(*value, U128::from(apply_percent(8, balance))); + } + } + } + + #[test] + fn validate_empty_inputs() { + let _ = Royalties::new(KEY_PREFIX, 0); + } + + #[test] + #[should_panic(expected = "royalty overflow")] + fn create_payout_overflow() { + let mut map = TreeMap::new(KEY_PREFIX); + + for idx in 0..10 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + } + + let royalties = Royalties::new(KEY_PREFIX, 100); + + royalties.create_payout(Balance::MAX, &AccountId::new_unchecked("alice".to_string())); + } + + #[test] + #[should_panic(expected = "can only have a maximum of 10 accounts spliting royalties")] + fn validate_too_many_accounts() { + let mut map = TreeMap::new(KEY_PREFIX); + + // Fails with 11 accounts. + for idx in 0..11 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + } + + let mut royalties = Royalties::new(KEY_PREFIX, 100); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + } + + #[test] + #[should_panic(expected = "each royalty should be at most 100")] + fn validate_roalty_per_account_fails() { + let mut map = TreeMap::new(KEY_PREFIX); + + // Fails with more than 100% per account. + map.insert(&AccountId::new_unchecked("bob".to_string()), &101); + + let mut royalties = Royalties::new(KEY_PREFIX, 100); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + } + + #[test] + #[should_panic(expected = "total percent of each royalty split must be at most 100")] + fn validate_total_roalties_fails() { + let mut map = TreeMap::new(KEY_PREFIX); + + // Fails with total royalties over 100%. + for idx in 0..10 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &11); + } + let mut royalties = Royalties::new(KEY_PREFIX, 100); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + } + + #[test] + #[should_panic(expected = "royalty percent must be between 0 - 100")] + fn validate_royalty_base_percent_fails() { + let mut map = TreeMap::new(KEY_PREFIX); + + // Fails with total royalties over 100%. + for idx in 0..10 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &11); + } + let mut royalties = Royalties::new(KEY_PREFIX, 101); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + } +} From 5c20528ebea0d51e99fa6c4a074a80871f81675d Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 24 Oct 2023 13:10:09 +0200 Subject: [PATCH 24/35] fix lint --- .../src/non_fungible_token/payout/payout_impl.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 9003d86b0..02c712e97 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -139,7 +139,7 @@ mod tests { // Make sure we don't overflow and don't end up with mismatched results due to using int as // opposed to float. - let balance = Balance::MAX / 100_00 * 100; + let balance = Balance::MAX / 10_000 * 100; let owner_id = AccountId::new_unchecked("alice".to_string()); let payout = royalties.create_payout(balance, &owner_id); for (key, value) in payout.payout.iter() { From 2db048b3f17b6fdc9360cff4ffa012b9be664e5b Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Sat, 28 Oct 2023 11:41:54 +0200 Subject: [PATCH 25/35] fix nits --- .../src/non_fungible_token/payout/payout_impl.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 02c712e97..abf057d6b 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -189,7 +189,7 @@ mod tests { #[test] #[should_panic(expected = "each royalty should be at most 100")] - fn validate_roalty_per_account_fails() { + fn validate_royalty_per_account_fails() { let mut map = TreeMap::new(KEY_PREFIX); // Fails with more than 100% per account. @@ -203,7 +203,7 @@ mod tests { #[test] #[should_panic(expected = "total percent of each royalty split must be at most 100")] - fn validate_total_roalties_fails() { + fn validate_total_royalties_fails() { let mut map = TreeMap::new(KEY_PREFIX); // Fails with total royalties over 100%. From 279a7eedb8806f9362bc2b8b4e9a2ff2a9059379 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 3 Nov 2023 18:04:26 +0100 Subject: [PATCH 26/35] simplify royalties and more tests --- .../src/non_fungible_token/core/core_impl.rs | 5 +- .../src/non_fungible_token/payout/mod.rs | 2 - .../non_fungible_token/payout/payout_impl.rs | 76 +++++++++++-------- 3 files changed, 44 insertions(+), 39 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/core/core_impl.rs b/near-contract-standards/src/non_fungible_token/core/core_impl.rs index 6951ca5d0..6dfcc9a28 100644 --- a/near-contract-standards/src/non_fungible_token/core/core_impl.rs +++ b/near-contract-standards/src/non_fungible_token/core/core_impl.rs @@ -92,10 +92,7 @@ impl NonFungibleToken { tokens_per_owner: enumeration_prefix.map(LookupMap::new), approvals_by_id, next_approval_id_by_id, - royalties: royalties_prefix.map(|prefix| { - // TODO: Figure out where the percent has to be coming from. - Royalties::new(prefix, 0) - }), + royalties: royalties_prefix.map(|prefix| Royalties::new(prefix)), }; this.measure_min_token_storage_cost(); this diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index fd38daa75..433e20bad 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -24,8 +24,6 @@ pub struct Payout { pub struct Royalties { /// A mapping of accounts to the percentage of total royalty to be distributed. pub accounts: TreeMap, - /// Total percent of incoming balance used for royalties. - pub percent: BasisPoint, } /// An interface allowing non-fungible token contracts to request that financial contracts pay-out diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index abf057d6b..09bdba2d7 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -6,18 +6,17 @@ use near_sdk::{assert_one_yocto, env}; use near_sdk::{require, AccountId, Balance, IntoStorageKey}; impl Royalties { - pub fn new(key_prefix: S, percent: BasisPoint) -> Self + pub fn new(key_prefix: S) -> Self where S: IntoStorageKey, { let temp_accounts: TreeMap = TreeMap::new(key_prefix); - let this = Self { accounts: temp_accounts, percent }; + let this = Self { accounts: temp_accounts }; this.validate(); this } pub(crate) fn validate(&self) { - require!(self.percent <= 100, "royalty percent must be between 0 - 100"); require!( self.accounts.len() <= 10, "can only have a maximum of 10 accounts spliting royalties" @@ -31,17 +30,14 @@ impl Royalties { } pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { - let royalty_payment = apply_percent(self.percent, balance); let mut payout = Payout { payout: self .accounts .iter() - .map(|(account, percent)| { - (account.clone(), apply_percent(percent, royalty_payment).into()) - }) + .map(|(account, percent)| (account.clone(), apply_percent(percent, balance).into())) .collect(), }; - let rest = balance - royalty_payment; + let rest = balance - payout.payout.values().fold(0, |acc, &sum| acc + sum.0); let owner_payout: u128 = payout.payout.get(owner_id).map_or(0, |x| x.0) + rest; payout.payout.insert(owner_id.clone(), owner_payout.into()); payout @@ -106,20 +102,20 @@ mod tests { map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); } - let mut royalties = Royalties::new(KEY_PREFIX, 100); + let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); royalties.validate(); // Make sure that max royalties works. let owner_id = AccountId::new_unchecked("alice".to_string()); - let payout = royalties.create_payout(Balance::MAX / 100, &owner_id); + let payout = royalties.create_payout(1000, &owner_id); for (key, value) in payout.payout.iter() { map.contains_key(key); if *key == owner_id { assert_eq!(*value, U128::from(0)); } else { - assert_eq!(*value, U128::from(apply_percent(10, Balance::MAX / 100))); + assert_eq!(*value, U128::from(apply_percent(10, 1000))); } } } @@ -129,10 +125,10 @@ mod tests { let mut map = TreeMap::new(KEY_PREFIX); for idx in 0..10 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &8); } - let mut royalties = Royalties::new(KEY_PREFIX, 80); + let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); royalties.validate(); @@ -152,9 +148,38 @@ mod tests { } } + #[test] + fn validate_owner_rest_and_royalty_path() { + let mut map = TreeMap::new(KEY_PREFIX); + + for idx in 0..9 { + map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &8); + } + map.insert(&AccountId::new_unchecked("alice".to_string()), &8); + + let mut royalties = Royalties::new(KEY_PREFIX); + + mem::swap(&mut royalties.accounts, &mut map); + royalties.validate(); + + // Make sure we don't overflow and don't end up with mismatched results due to using int as + // opposed to float. + let balance = Balance::MAX / 10_000 * 100; + let owner_id = AccountId::new_unchecked("alice".to_string()); + let payout = royalties.create_payout(balance, &owner_id); + for (key, value) in payout.payout.iter() { + map.contains_key(key); + if *key == owner_id { + assert_eq!(*value, U128::from(apply_percent(28, balance))); + } else { + assert_eq!(*value, U128::from(apply_percent(8, balance))); + } + } + } + #[test] fn validate_empty_inputs() { - let _ = Royalties::new(KEY_PREFIX, 0); + let _ = Royalties::new(KEY_PREFIX); } #[test] @@ -166,7 +191,7 @@ mod tests { map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); } - let royalties = Royalties::new(KEY_PREFIX, 100); + let royalties = Royalties::new(KEY_PREFIX); royalties.create_payout(Balance::MAX, &AccountId::new_unchecked("alice".to_string())); } @@ -181,7 +206,7 @@ mod tests { map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); } - let mut royalties = Royalties::new(KEY_PREFIX, 100); + let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); royalties.validate(); @@ -195,7 +220,7 @@ mod tests { // Fails with more than 100% per account. map.insert(&AccountId::new_unchecked("bob".to_string()), &101); - let mut royalties = Royalties::new(KEY_PREFIX, 100); + let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); royalties.validate(); @@ -210,22 +235,7 @@ mod tests { for idx in 0..10 { map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &11); } - let mut royalties = Royalties::new(KEY_PREFIX, 100); - - mem::swap(&mut royalties.accounts, &mut map); - royalties.validate(); - } - - #[test] - #[should_panic(expected = "royalty percent must be between 0 - 100")] - fn validate_royalty_base_percent_fails() { - let mut map = TreeMap::new(KEY_PREFIX); - - // Fails with total royalties over 100%. - for idx in 0..10 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &11); - } - let mut royalties = Royalties::new(KEY_PREFIX, 101); + let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); royalties.validate(); From 369faaab007572752a326abe5aff4f6857e20205 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 3 Nov 2023 18:09:34 +0100 Subject: [PATCH 27/35] add comment --- .../src/non_fungible_token/payout/payout_impl.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 09bdba2d7..c22306a87 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -29,6 +29,14 @@ impl Royalties { require!(total <= 100, "total percent of each royalty split must be at most 100") } + /// Create a payout. + /// + /// # Arguments + /// * `balance` - total balance dedicated to the payout. + /// * `owner_id` - nft owner account id + /// + /// NOTE: The owner gets whatever is left after distributing the rest of the payout plus the + /// percentage specified explicitly, if any. pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { let mut payout = Payout { payout: self From 9e01a091da0d916dede8255ffd8c3ab5766a21ab Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 3 Nov 2023 18:36:03 +0100 Subject: [PATCH 28/35] validate apply_percent --- .../src/non_fungible_token/payout/payout_impl.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index c22306a87..fceecaa37 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -52,7 +52,6 @@ impl Royalties { } } -// TODO: Perhaps redo this function so that it never overflows. fn apply_percent(percent: BasisPoint, int: u128) -> u128 { int.checked_mul(percent as u128).unwrap_or_else(|| env::panic_str("royalty overflow")) / 100u128 } @@ -204,6 +203,12 @@ mod tests { royalties.create_payout(Balance::MAX, &AccountId::new_unchecked("alice".to_string())); } + #[test] + #[should_panic(expected = "royalty overflow")] + fn apply_percent_overflow() { + apply_percent(10, Balance::MAX); + } + #[test] #[should_panic(expected = "can only have a maximum of 10 accounts spliting royalties")] fn validate_too_many_accounts() { From 423bdc941af216ada36b684b79ca130ed2a2ff82 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 3 Nov 2023 18:48:51 +0100 Subject: [PATCH 29/35] fix --- .../src/non_fungible_token/payout/payout_impl.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index fceecaa37..413a1f086 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -198,7 +198,8 @@ mod tests { map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); } - let royalties = Royalties::new(KEY_PREFIX); + let mut royalties = Royalties::new(KEY_PREFIX); + mem::swap(&mut royalties.accounts, &mut map); royalties.create_payout(Balance::MAX, &AccountId::new_unchecked("alice".to_string())); } From 5829c77d6349ffc04726217f9a339c22c9f6001e Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 10 Nov 2023 16:42:40 +0100 Subject: [PATCH 30/35] fix tests --- .../non_fungible_token/payout/payout_impl.rs | 31 ++++++++++--------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index 413a1f086..f7fbaf50e 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -93,9 +93,10 @@ impl NonFungibleTokenPayout for NonFungibleToken { mod tests { use crate::non_fungible_token::payout::payout_impl::apply_percent; use crate::non_fungible_token::payout::Royalties; + use near_account_id::AccountIdRef; use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; - use near_sdk::{AccountId, Balance}; + use near_sdk::Balance; use std::mem; const KEY_PREFIX: &[u8] = "test_prefix".as_bytes(); @@ -106,7 +107,7 @@ mod tests { // Works with up to 100% and at most 10 accounts. for idx in 0..10 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &10); } let mut royalties = Royalties::new(KEY_PREFIX); @@ -115,7 +116,7 @@ mod tests { royalties.validate(); // Make sure that max royalties works. - let owner_id = AccountId::new_unchecked("alice".to_string()); + let owner_id = AccountIdRef::new_or_panic("alice").into(); let payout = royalties.create_payout(1000, &owner_id); for (key, value) in payout.payout.iter() { map.contains_key(key); @@ -132,7 +133,7 @@ mod tests { let mut map = TreeMap::new(KEY_PREFIX); for idx in 0..10 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &8); + map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &8); } let mut royalties = Royalties::new(KEY_PREFIX); @@ -143,8 +144,8 @@ mod tests { // Make sure we don't overflow and don't end up with mismatched results due to using int as // opposed to float. let balance = Balance::MAX / 10_000 * 100; - let owner_id = AccountId::new_unchecked("alice".to_string()); - let payout = royalties.create_payout(balance, &owner_id); + let owner_id = AccountIdRef::new_or_panic("alice"); + let payout = royalties.create_payout(balance, &owner_id.into()); for (key, value) in payout.payout.iter() { map.contains_key(key); if *key == owner_id { @@ -160,9 +161,9 @@ mod tests { let mut map = TreeMap::new(KEY_PREFIX); for idx in 0..9 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &8); + map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &8); } - map.insert(&AccountId::new_unchecked("alice".to_string()), &8); + map.insert(&AccountIdRef::new_or_panic("alice").into(), &8); let mut royalties = Royalties::new(KEY_PREFIX); @@ -172,8 +173,8 @@ mod tests { // Make sure we don't overflow and don't end up with mismatched results due to using int as // opposed to float. let balance = Balance::MAX / 10_000 * 100; - let owner_id = AccountId::new_unchecked("alice".to_string()); - let payout = royalties.create_payout(balance, &owner_id); + let owner_id = AccountIdRef::new_or_panic("alice"); + let payout = royalties.create_payout(balance, &owner_id.into()); for (key, value) in payout.payout.iter() { map.contains_key(key); if *key == owner_id { @@ -195,13 +196,13 @@ mod tests { let mut map = TreeMap::new(KEY_PREFIX); for idx in 0..10 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &10); } let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); - royalties.create_payout(Balance::MAX, &AccountId::new_unchecked("alice".to_string())); + royalties.create_payout(Balance::MAX, &AccountIdRef::new_or_panic("alice").into()); } #[test] @@ -217,7 +218,7 @@ mod tests { // Fails with 11 accounts. for idx in 0..11 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &10); + map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &10); } let mut royalties = Royalties::new(KEY_PREFIX); @@ -232,7 +233,7 @@ mod tests { let mut map = TreeMap::new(KEY_PREFIX); // Fails with more than 100% per account. - map.insert(&AccountId::new_unchecked("bob".to_string()), &101); + map.insert(&AccountIdRef::new_or_panic("bob").into(), &101); let mut royalties = Royalties::new(KEY_PREFIX); @@ -247,7 +248,7 @@ mod tests { // Fails with total royalties over 100%. for idx in 0..10 { - map.insert(&AccountId::new_unchecked(format!("bob_{}", idx)), &11); + map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &11); } let mut royalties = Royalties::new(KEY_PREFIX); From 312f4ac79c2927c4c6315f5e720e0227837838d3 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 24 Nov 2023 16:11:42 +0100 Subject: [PATCH 31/35] fix payouts --- .../src/non_fungible_token/payout/mod.rs | 5 +- .../non_fungible_token/payout/payout_impl.rs | 113 ++++++++++++++---- 2 files changed, 92 insertions(+), 26 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 433e20bad..012d847a7 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -1,5 +1,6 @@ mod payout_impl; +use crate::non_fungible_token::TokenId; use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; @@ -22,8 +23,8 @@ pub struct Payout { #[derive(BorshDeserialize, BorshSerialize, Debug)] #[borsh(crate = "near_sdk::borsh")] pub struct Royalties { - /// A mapping of accounts to the percentage of total royalty to be distributed. - pub accounts: TreeMap, + /// A mapping of accounts to the percentage of total royalty to be distributed per token. + pub accounts: TreeMap>, } /// An interface allowing non-fungible token contracts to request that financial contracts pay-out diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index f7fbaf50e..cb58fef60 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -1,16 +1,18 @@ use super::NonFungibleTokenPayout; +use crate::fungible_token::Balance; use crate::non_fungible_token::core::NonFungibleTokenCore; use crate::non_fungible_token::payout::*; use crate::non_fungible_token::NonFungibleToken; use near_sdk::{assert_one_yocto, env}; -use near_sdk::{require, AccountId, Balance, IntoStorageKey}; +use near_sdk::{require, AccountId, IntoStorageKey}; impl Royalties { pub fn new(key_prefix: S) -> Self where S: IntoStorageKey, { - let temp_accounts: TreeMap = TreeMap::new(key_prefix); + let temp_accounts: TreeMap> = + TreeMap::new(key_prefix); let this = Self { accounts: temp_accounts }; this.validate(); this @@ -21,28 +23,53 @@ impl Royalties { self.accounts.len() <= 10, "can only have a maximum of 10 accounts spliting royalties" ); - let mut total: BasisPoint = 0; - self.accounts.iter().for_each(|(_, percent)| { - require!(percent <= 100, "each royalty should be at most 100"); - total += percent; + + let mut total_per_token = HashMap::new(); + + self.accounts.iter().for_each(|(_, percent_per_token)| { + percent_per_token.iter().for_each(|(token_id, percent)| { + require!(*percent <= 100, "each royalty should be at most 100"); + *total_per_token.entry(token_id.to_owned()).or_default() += percent; + }); + }); + + total_per_token.values().for_each(|total: &u16| { + require!( + *total <= 100, + "total percent of each royalty split must be at most 100 per token" + ) }); - require!(total <= 100, "total percent of each royalty split must be at most 100") } /// Create a payout. /// /// # Arguments + /// * `token_id` - token_id for payout. /// * `balance` - total balance dedicated to the payout. - /// * `owner_id` - nft owner account id + /// * `owner_id` - nft owner account id. /// /// NOTE: The owner gets whatever is left after distributing the rest of the payout plus the /// percentage specified explicitly, if any. - pub fn create_payout(&self, balance: Balance, owner_id: &AccountId) -> Payout { + pub fn create_payout( + &self, + token_id: TokenId, + balance: Balance, + owner_id: &AccountId, + ) -> Payout { let mut payout = Payout { payout: self .accounts .iter() - .map(|(account, percent)| (account.clone(), apply_percent(percent, balance).into())) + .map(|(account, percent_per_token)| { + ( + account.clone(), + U128::from(apply_percent( + *percent_per_token.get(&token_id).unwrap_or(&0), + balance, + )), + ) + }) + .filter(|(_, payout)| payout.0 > 0) .collect(), }; let rest = balance - payout.payout.values().fold(0, |acc, &sum| acc + sum.0); @@ -62,7 +89,7 @@ impl NonFungibleTokenPayout for NonFungibleToken { let payout = self .royalties .as_ref() - .map_or(Payout::default(), |r| r.create_payout(balance.0, &owner_id)); + .map_or(Payout::default(), |r| r.create_payout(token_id, balance.0, &owner_id)); if let Some(max_len_payout) = max_len_payout { require!( @@ -91,12 +118,13 @@ impl NonFungibleTokenPayout for NonFungibleToken { } #[cfg(test)] mod tests { + use crate::fungible_token::Balance; use crate::non_fungible_token::payout::payout_impl::apply_percent; use crate::non_fungible_token::payout::Royalties; use near_account_id::AccountIdRef; use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; - use near_sdk::Balance; + use std::collections::HashMap; use std::mem; const KEY_PREFIX: &[u8] = "test_prefix".as_bytes(); @@ -104,10 +132,14 @@ mod tests { #[test] fn validate_happy_path() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); // Works with up to 100% and at most 10 accounts. for idx in 0..10 { - map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &10); + map.insert( + &AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), + &HashMap::from([(token_id.clone(), 10)]), + ); } let mut royalties = Royalties::new(KEY_PREFIX); @@ -117,7 +149,7 @@ mod tests { // Make sure that max royalties works. let owner_id = AccountIdRef::new_or_panic("alice").into(); - let payout = royalties.create_payout(1000, &owner_id); + let payout = royalties.create_payout(token_id, 1000, &owner_id); for (key, value) in payout.payout.iter() { map.contains_key(key); if *key == owner_id { @@ -131,9 +163,13 @@ mod tests { #[test] fn validate_owner_rest_path() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); for idx in 0..10 { - map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &8); + map.insert( + &AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), + &HashMap::from([(token_id.clone(), 8)]), + ); } let mut royalties = Royalties::new(KEY_PREFIX); @@ -145,7 +181,7 @@ mod tests { // opposed to float. let balance = Balance::MAX / 10_000 * 100; let owner_id = AccountIdRef::new_or_panic("alice"); - let payout = royalties.create_payout(balance, &owner_id.into()); + let payout = royalties.create_payout(token_id, balance, &owner_id.into()); for (key, value) in payout.payout.iter() { map.contains_key(key); if *key == owner_id { @@ -159,11 +195,19 @@ mod tests { #[test] fn validate_owner_rest_and_royalty_path() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); for idx in 0..9 { - map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &8); + map.insert( + &AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), + &HashMap::from([(token_id.clone(), 8)]), + ); } - map.insert(&AccountIdRef::new_or_panic("alice").into(), &8); + + map.insert( + &AccountIdRef::new_or_panic("alice").into(), + &HashMap::from([(token_id.clone(), 8)]), + ); let mut royalties = Royalties::new(KEY_PREFIX); @@ -174,7 +218,7 @@ mod tests { // opposed to float. let balance = Balance::MAX / 10_000 * 100; let owner_id = AccountIdRef::new_or_panic("alice"); - let payout = royalties.create_payout(balance, &owner_id.into()); + let payout = royalties.create_payout(token_id, balance, &owner_id.into()); for (key, value) in payout.payout.iter() { map.contains_key(key); if *key == owner_id { @@ -194,15 +238,23 @@ mod tests { #[should_panic(expected = "royalty overflow")] fn create_payout_overflow() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); for idx in 0..10 { - map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &10); + map.insert( + &AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), + &HashMap::from([(token_id.clone(), 8)]), + ); } let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); - royalties.create_payout(Balance::MAX, &AccountIdRef::new_or_panic("alice").into()); + royalties.create_payout( + token_id, + Balance::MAX, + &AccountIdRef::new_or_panic("alice").into(), + ); } #[test] @@ -215,10 +267,14 @@ mod tests { #[should_panic(expected = "can only have a maximum of 10 accounts spliting royalties")] fn validate_too_many_accounts() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); // Fails with 11 accounts. for idx in 0..11 { - map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &10); + map.insert( + &AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), + &HashMap::from([(token_id.clone(), 8)]), + ); } let mut royalties = Royalties::new(KEY_PREFIX); @@ -231,9 +287,13 @@ mod tests { #[should_panic(expected = "each royalty should be at most 100")] fn validate_royalty_per_account_fails() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); // Fails with more than 100% per account. - map.insert(&AccountIdRef::new_or_panic("bob").into(), &101); + map.insert( + &AccountIdRef::new_or_panic("bob").into(), + &HashMap::from([(token_id.clone(), 101)]), + ); let mut royalties = Royalties::new(KEY_PREFIX); @@ -245,11 +305,16 @@ mod tests { #[should_panic(expected = "total percent of each royalty split must be at most 100")] fn validate_total_royalties_fails() { let mut map = TreeMap::new(KEY_PREFIX); + let token_id = "token_id".to_string(); // Fails with total royalties over 100%. for idx in 0..10 { - map.insert(&AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), &11); + map.insert( + &AccountIdRef::new_or_panic(&format!("bob_{}", idx)).into(), + &HashMap::from([(token_id.clone(), 11)]), + ); } + let mut royalties = Royalties::new(KEY_PREFIX); mem::swap(&mut royalties.accounts, &mut map); From f2e709cd5ac0bf3bba6ff1bf8d68e1e731d3b767 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Fri, 24 Nov 2023 19:22:08 +0100 Subject: [PATCH 32/35] fix NearToken --- examples/non-fungible-token/nft/src/lib.rs | 4 ++-- examples/non-fungible-token/tests/workspaces/test_payout.rs | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/examples/non-fungible-token/nft/src/lib.rs b/examples/non-fungible-token/nft/src/lib.rs index 0481e2508..abd42f57e 100644 --- a/examples/non-fungible-token/nft/src/lib.rs +++ b/examples/non-fungible-token/nft/src/lib.rs @@ -378,7 +378,7 @@ mod tests { testing_env!(context .storage_usage(env::storage_usage()) - .attached_deposit(1) + .attached_deposit(NearToken::from_yoctonear(1)) .predecessor_account_id(accounts(0)) .build()); @@ -400,7 +400,7 @@ mod tests { .storage_usage(env::storage_usage()) .account_balance(env::account_balance()) .is_view(true) - .attached_deposit(0) + .attached_deposit(NearToken::from_near(0)) .build()); if let Some(token) = contract.nft_token(token_id.clone()) { diff --git a/examples/non-fungible-token/tests/workspaces/test_payout.rs b/examples/non-fungible-token/tests/workspaces/test_payout.rs index d73e04abf..8bd1442a9 100644 --- a/examples/non-fungible-token/tests/workspaces/test_payout.rs +++ b/examples/non-fungible-token/tests/workspaces/test_payout.rs @@ -1,7 +1,9 @@ use crate::utils::{init, TOKEN_ID}; use near_contract_standards::non_fungible_token::Token; use near_sdk::json_types::U128; -use near_sdk::ONE_YOCTO; +use near_sdk::NearToken; + +const ONE_YOCTO: NearToken = NearToken::from_yoctonear(1); #[tokio::test] async fn simulate_payout() -> anyhow::Result<()> { From 66ccf2ca540156a1dcdf6ee33402af34881574cc Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 12 Dec 2023 18:43:39 +0100 Subject: [PATCH 33/35] fix workflow name --- .github/workflows/test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e32c5bfdd..dd83c0343 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,7 +23,7 @@ jobs: toolchain: ${{ matrix.toolchain }} default: true target: wasm32-unknown-unknown - - name: downgrade `anstyle`,`anstyle-parse`,`clap`,`clap_lex` crates to support older Rust toolchain + - name: downgrade `anstyle`,`anstyle-parse`,`anstyle-query`,`clap`,`clap_lex` crates to support older Rust toolchain if: matrix.toolchain == '1.69.0' run: | cargo update -p anstyle --precise 1.0.2 From 1fa86f71cea0010ca64baecce25bfe6170e48285 Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Tue, 12 Dec 2023 18:38:11 +0100 Subject: [PATCH 34/35] fix --- .github/workflows/test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dd83c0343..f69296b0f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -27,6 +27,7 @@ jobs: if: matrix.toolchain == '1.69.0' run: | cargo update -p anstyle --precise 1.0.2 + cargo update -p anstyle-query --precise 1.0.0 cargo update -p anstyle-parse --precise 0.2.1 cargo update -p clap --precise 4.3.24 cargo update -p clap_lex --precise 0.5.0 From 5a574b43c033af1fe0159246181c2c97467b156e Mon Sep 17 00:00:00 2001 From: Roman Useinov Date: Wed, 13 Dec 2023 12:08:14 +0100 Subject: [PATCH 35/35] fix compilation --- near-contract-standards/src/non_fungible_token/payout/mod.rs | 2 +- .../src/non_fungible_token/payout/payout_impl.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/near-contract-standards/src/non_fungible_token/payout/mod.rs b/near-contract-standards/src/non_fungible_token/payout/mod.rs index 012d847a7..3b5f7a1b2 100644 --- a/near-contract-standards/src/non_fungible_token/payout/mod.rs +++ b/near-contract-standards/src/non_fungible_token/payout/mod.rs @@ -4,8 +4,8 @@ use crate::non_fungible_token::TokenId; use near_sdk::borsh::{BorshDeserialize, BorshSerialize}; use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; +use near_sdk::serde::{Deserialize, Serialize}; use near_sdk::AccountId; -use serde::{Deserialize, Serialize}; use std::collections::HashMap; type BasisPoint = u16; diff --git a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs index cb58fef60..82d664b29 100644 --- a/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs +++ b/near-contract-standards/src/non_fungible_token/payout/payout_impl.rs @@ -121,9 +121,9 @@ mod tests { use crate::fungible_token::Balance; use crate::non_fungible_token::payout::payout_impl::apply_percent; use crate::non_fungible_token::payout::Royalties; - use near_account_id::AccountIdRef; use near_sdk::collections::TreeMap; use near_sdk::json_types::U128; + use near_sdk::AccountIdRef; use std::collections::HashMap; use std::mem;