diff --git a/Cargo.lock b/Cargo.lock index 960e68f5..5f64fe92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3403,6 +3403,26 @@ dependencies = [ "syn 2.0.66", ] +[[package]] +name = "enum-iterator" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c280b9e6b3ae19e152d8e31cf47f18389781e119d4013a2a2bb0180e5facc635" +dependencies = [ + "enum-iterator-derive", +] + +[[package]] +name = "enum-iterator-derive" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ab991c1362ac86c61ab6f556cff143daa22e5a15e4e189df818b2fd19fe65b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "enumflags2" version = "0.7.10" @@ -10573,6 +10593,7 @@ dependencies = [ name = "pop-primitives" version = "0.0.0" dependencies = [ + "enum-iterator", "parity-scale-codec", "scale-info", ] diff --git a/extension/src/functions.rs b/extension/src/functions.rs index 5297f256..73a5fdf9 100644 --- a/extension/src/functions.rs +++ b/extension/src/functions.rs @@ -149,7 +149,7 @@ pub trait Readable { /// Trait for fallible conversion of a value based on additional information available from the /// environment. pub trait Converter { - /// The type returned in the event of a conversion error. + /// The error type returned when conversion fails. type Error; /// The type of value to be converted. type Source; @@ -173,7 +173,7 @@ pub trait Converter { /// A default converter, for converting (encoding) from some type into a byte array. pub struct DefaultConverter(PhantomData); impl>> Converter for DefaultConverter { - /// The type returned in the event of a conversion error. + /// The error type returned when conversion fails. type Error = DispatchError; /// The type of value to be converted. type Source = T; @@ -476,7 +476,7 @@ mod tests { Ok(Converging(0)) )); // Check if the contract environment buffer is written correctly. - assert_eq!(env.buffer, UppercaseConverter::try_convert(expected, &env).unwrap()); + assert_eq!(Ok(&env.buffer), UppercaseConverter::try_convert(expected, &env).as_ref()); } #[test] @@ -552,6 +552,9 @@ mod tests { fn default_conversion_works() { let env = MockEnvironment::default(); let source = "pop".to_string(); - assert_eq!(DefaultConverter::try_convert(source.clone(), &env).unwrap(), source.as_bytes()); + assert_eq!( + DefaultConverter::try_convert(source.clone(), &env), + Ok(source.as_bytes().into()) + ); } } diff --git a/pallets/api/src/extension.rs b/pallets/api/src/extension.rs index ff0dde6b..0fd22f2c 100644 --- a/pallets/api/src/extension.rs +++ b/pallets/api/src/extension.rs @@ -112,7 +112,7 @@ impl + Debug /// - `value` - The value to be converted. /// - `env` - The current execution environment. fn try_convert(value: Self::Source, env: &impl Environment) -> Result { - // Defer to supplied versioned result conversion type + // Defer to supplied versioned result conversion type. let version = version(env); log::debug!(target: Self::LOG_TARGET, "versioned result converter: result={value:?}, version={version}"); let converted: Target = (value, version).try_into()?; @@ -163,12 +163,16 @@ mod tests { #[test] fn prepender_works() { + let func = 0; + let version = 1; + let module = 2; + let index = 3; let env = MockEnvironment { - func_id: u16::from_le_bytes([1, 2]), - ext_id: u16::from_le_bytes([3, 4]), + func_id: u16::from_le_bytes([func, version]), + ext_id: u16::from_le_bytes([module, index]), }; - assert_eq!(Prepender::process(vec![0u8], &env), vec![2, 3, 4, 0]); - assert_eq!(Prepender::process(vec![0u8, 5, 10], &env), vec![2, 3, 4, 0, 5, 10]); + let data = 42; + assert_eq!(Prepender::process(vec![data], &env), vec![version, module, index, data]); } #[test] @@ -199,9 +203,8 @@ mod tests { )); } - #[test] - fn versioned_error_converter_works() { - use super::RetVal::Converging; + mod versioned_error { + use super::{RetVal::Converging, *}; // Mock versioned error. #[derive(Debug)] @@ -239,36 +242,41 @@ mod tests { } } - // Because `Retval` does not implement the `Debug` trait the success and error test cases - // are separated. - for (version, error, expected_result) in vec![ - (0, BadOrigin, 1), - (0, CannotLookup, 100), - (1, BadOrigin, 2), - (1, CannotLookup, 200), - ] { - let env = MockEnvironment { func_id: u16::from_le_bytes([0, version]), ext_id: 0u16 }; - // Again, due to missing `Debug` trait the result has to be unwrapped. - let Ok(Converging(result)) = - VersionedErrorConverter::::convert(error, &env) - else { - panic!("should not happen") - }; - assert_eq!(result, expected_result); + #[test] + fn versioned_error_converter_works() { + for (version, error, expected_result) in vec![ + (0, BadOrigin, 1), + (0, Other("test"), 100), + (1, BadOrigin, 2), + (1, Other("test"), 200), + ] { + let env = + MockEnvironment { func_id: u16::from_le_bytes([0, version]), ext_id: 0u16 }; + // Because `Retval` does not implement the `Debug` trait the result has to be + // unwrapped. + let Ok(Converging(result)) = + VersionedErrorConverter::::convert(error, &env) + else { + panic!("should not happen") + }; + assert_eq!(result, expected_result); + } } - // Error case. - let env = MockEnvironment { func_id: u16::from_le_bytes([0, 2]), ext_id: 0u16 }; - let result = VersionedErrorConverter::::convert(BadOrigin, &env) - .err() - .unwrap(); // Again, can't use `unwrap_err()` due to missing `Debug` trait. - assert_eq!(result, Other("DecodingFailed")); + #[test] + fn versioned_error_converter_fails_when_invalid_version() { + let version = 2; + let env = MockEnvironment { func_id: u16::from_le_bytes([0, version]), ext_id: 0u16 }; + let result = VersionedErrorConverter::::convert(BadOrigin, &env).err(); + assert_eq!(result, Some(Other("DecodingFailed"))); + } } - #[test] - fn versioned_result_converter_works() { + mod versioned_result { use VersionedRuntimeResult::{V0, V1}; + use super::*; + // Mock versioned runtime result. #[derive(Debug, PartialEq)] pub enum VersionedRuntimeResult { @@ -282,6 +290,7 @@ mod tests { // Mock conversion based on result and version. fn try_from(value: (u8, u8)) -> Result { let (result, version) = value; + // Per version there is a specific upper bound allowed. match version { 0 if result <= 50 => Ok(V0(result)), 0 if result > 50 => Ok(V0(50)), @@ -292,17 +301,31 @@ mod tests { } } - for (version, value, expected_result) in vec![ - (0, 10, Ok(V0(10))), - (0, 100, Ok(V0(50))), - (1, 10, Ok(V1(10))), - (1, 100, Ok(V1(100))), - (2, 10, Err(Other("DecodingFailed"))), - ] { - let env = MockEnvironment { func_id: u16::from_le_bytes([0, version]), ext_id: 0u16 }; + #[test] + fn versioned_result_converter_works() { + for (version, value, expected_result) in vec![ + (0, 10, Ok(V0(10))), + // `V0` has 50 as upper bound and therefore caps the value. + (0, 100, Ok(V0(50))), + (1, 10, Ok(V1(10))), + // Different upper bound for `V1`. + (1, 100, Ok(V1(100))), + ] { + let env = + MockEnvironment { func_id: u16::from_le_bytes([0, version]), ext_id: 0u16 }; + let result = VersionedResultConverter::::try_convert( + value, &env, + ); + assert_eq!(result, expected_result); + } + } + + #[test] + fn versioned_result_converter_fails_when_invalid_version() { + let env = MockEnvironment { func_id: u16::from_le_bytes([0, 2]), ext_id: 0u16 }; let result = - VersionedResultConverter::::try_convert(value, &env); - assert_eq!(result, expected_result); + VersionedResultConverter::::try_convert(10, &env).err(); + assert_eq!(result, Some(Other("DecodingFailed"))); } } diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs index e143ae47..7ded38c0 100644 --- a/pallets/api/src/fungibles/benchmarking.rs +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -14,7 +14,10 @@ use frame_support::{ use frame_system::RawOrigin; use sp_runtime::traits::Zero; -use super::{AccountIdOf, AssetsInstanceOf, AssetsOf, BalanceOf, Call, Config, Pallet, TokenIdOf}; +use super::{ + AccountIdOf, AssetsInstanceOf, AssetsOf, BalanceOf, Call, Config, Pallet, Read, TokenIdOf, +}; +use crate::Read as _; const SEED: u32 = 1; @@ -103,5 +106,73 @@ mod benchmarks { Ok(()) } + #[benchmark] + // Storage: `Assets` + fn total_supply() { + #[block] + { + Pallet::::read(Read::TotalSupply(TokenIdOf::::zero())); + } + } + #[benchmark] + // Storage: `AssetAccount` + fn balance_of() { + #[block] + { + Pallet::::read(Read::BalanceOf { + token: TokenIdOf::::zero(), + owner: account("Alice", 0, SEED), + }); + } + } + #[benchmark] + // Storage: `Approval` + fn allowance() { + #[block] + { + Pallet::::read(Read::Allowance { + token: TokenIdOf::::zero(), + owner: account("Alice", 0, SEED), + spender: account("Bob", 0, SEED), + }); + } + } + + #[benchmark] + // Storage: `AssetMetadata` + fn token_name() { + #[block] + { + Pallet::::read(Read::TokenName(TokenIdOf::::zero())); + } + } + + #[benchmark] + // Storage: `AssetMetadata` + fn token_symbol() { + #[block] + { + Pallet::::read(Read::TokenSymbol(TokenIdOf::::zero())); + } + } + + #[benchmark] + // Storage: `AssetMetadata` + fn token_decimals() { + #[block] + { + Pallet::::read(Read::TokenDecimals(TokenIdOf::::zero())); + } + } + + #[benchmark] + // Storage: `Assets` + fn token_exists() { + #[block] + { + Pallet::::read(Read::TokenExists(TokenIdOf::::zero())); + } + } + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); } diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index e38a8980..c6fe593c 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -41,7 +41,8 @@ pub mod pallet { use super::*; /// State reads for the fungibles API with required input. - #[derive(Encode, Decode, Debug, MaxEncodedLen, PartialEq, Clone)] + #[derive(Encode, Decode, Debug, MaxEncodedLen)] + #[cfg_attr(feature = "std", derive(PartialEq, Clone))] #[repr(u8)] #[allow(clippy::unnecessary_cast)] pub enum Read { @@ -81,7 +82,8 @@ pub mod pallet { } /// Results of state reads for the fungibles API. - #[derive(Debug, PartialEq, Clone)] + #[derive(Debug)] + #[cfg_attr(feature = "std", derive(PartialEq, Clone))] pub enum ReadResult { /// Total token supply for a specified token. TotalSupply(BalanceOf), @@ -485,9 +487,17 @@ pub mod pallet { /// /// # Parameters /// - `request` - The read request. - fn weight(_request: &Self::Read) -> Weight { - // TODO: match on request and return benchmarked weight - T::DbWeight::get().reads(1_u64) + fn weight(request: &Self::Read) -> Weight { + use Read::*; + match request { + TotalSupply(_) => ::WeightInfo::total_supply(), + BalanceOf { .. } => ::WeightInfo::balance_of(), + Allowance { .. } => ::WeightInfo::allowance(), + TokenName(_) => ::WeightInfo::token_name(), + TokenSymbol(_) => ::WeightInfo::token_symbol(), + TokenDecimals(_) => ::WeightInfo::token_decimals(), + TokenExists(_) => ::WeightInfo::token_exists(), + } } /// Performs the requested read and returns the result. diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 747785c1..a3929551 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -578,3 +578,100 @@ mod assets { assert_ok!(Assets::set_metadata(signed(owner), token, name, symbol, decimals)); } } + +mod read_weights { + use frame_support::weights::Weight; + + use super::*; + use crate::fungibles::{weights::WeightInfo, Config}; + + struct ReadWeightInfo { + total_supply: Weight, + balance_of: Weight, + allowance: Weight, + token_name: Weight, + token_symbol: Weight, + token_decimals: Weight, + token_exists: Weight, + } + + impl ReadWeightInfo { + fn new() -> Self { + Self { + total_supply: Fungibles::weight(&TotalSupply(TOKEN)), + balance_of: Fungibles::weight(&BalanceOf { token: TOKEN, owner: ALICE }), + allowance: Fungibles::weight(&Allowance { + token: TOKEN, + owner: ALICE, + spender: BOB, + }), + token_name: Fungibles::weight(&TokenName(TOKEN)), + token_symbol: Fungibles::weight(&TokenSymbol(TOKEN)), + token_decimals: Fungibles::weight(&TokenDecimals(TOKEN)), + token_exists: Fungibles::weight(&TokenExists(TOKEN)), + } + } + } + + #[test] + fn ensure_read_matches_benchmarks() { + let ReadWeightInfo { + allowance, + balance_of, + token_decimals, + token_name, + token_symbol, + total_supply, + token_exists, + } = ReadWeightInfo::new(); + + assert_eq!(total_supply, ::WeightInfo::total_supply()); + assert_eq!(balance_of, ::WeightInfo::balance_of()); + assert_eq!(allowance, ::WeightInfo::allowance()); + assert_eq!(token_name, ::WeightInfo::token_name()); + assert_eq!(token_symbol, ::WeightInfo::token_symbol()); + assert_eq!(token_decimals, ::WeightInfo::token_decimals()); + assert_eq!(token_exists, ::WeightInfo::token_exists()); + } + + // These types read from the `AssetMetadata` storage. + #[test] + fn ensure_asset_metadata_variants_match() { + let ReadWeightInfo { token_decimals, token_name, token_symbol, .. } = ReadWeightInfo::new(); + + assert_eq!(token_decimals, token_name); + assert_eq!(token_decimals, token_symbol); + } + + // These types read from the `Assets` storage. + #[test] + fn ensure_asset_variants_match() { + let ReadWeightInfo { total_supply, token_exists, .. } = ReadWeightInfo::new(); + + assert_eq!(total_supply, token_exists); + } + + // Proof size is based on `MaxEncodedLen`, not hardware. + // This test ensures that the data structure sizes do not change with upgrades. + #[test] + fn ensure_expected_proof_size_does_not_change() { + let ReadWeightInfo { + allowance, + balance_of, + token_decimals, + token_name, + token_symbol, + total_supply, + token_exists, + } = ReadWeightInfo::new(); + + // These values come from `weights.rs`. + assert_eq!(allowance.proof_size(), 3613); + assert_eq!(balance_of.proof_size(), 3599); + assert_eq!(token_name.proof_size(), 3605); + assert_eq!(token_symbol.proof_size(), 3605); + assert_eq!(token_decimals.proof_size(), 3605); + assert_eq!(total_supply.proof_size(), 3675); + assert_eq!(token_exists.proof_size(), 3675); + } +} diff --git a/pallets/api/src/fungibles/weights.rs b/pallets/api/src/fungibles/weights.rs index a6c31654..28c5a6b5 100644 --- a/pallets/api/src/fungibles/weights.rs +++ b/pallets/api/src/fungibles/weights.rs @@ -1,8 +1,8 @@ -//! Autogenerated weights for `pallet_api::fungibles` +//! Autogenerated weights for `fungibles` //! -//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 33.0.0 -//! DATE: 2024-07-25, STEPS: `20`, REPEAT: `5`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 40.0.0 +//! DATE: 2024-09-13, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `R0GUE`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` @@ -13,9 +13,9 @@ // pallet // --chain=dev // --wasm-execution=compiled -// --pallet=pallet_api::fungibles -// --steps=20 -// --repeat=5 +// --pallet=fungibles +// --steps=50 +// --repeat=20 // --json // --template // ./scripts/pallet-weights-template.hbs @@ -30,12 +30,19 @@ use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; use core::marker::PhantomData; -/// Weight functions needed for `pallet_api::fungibles`. +/// Weight functions needed for `fungibles`. pub trait WeightInfo { fn approve(a: u32, c: u32, ) -> Weight; + fn total_supply() -> Weight; + fn balance_of() -> Weight; + fn allowance() -> Weight; + fn token_name() -> Weight; + fn token_symbol() -> Weight; + fn token_decimals() -> Weight; + fn token_exists() -> Weight; } -/// Weights for `pallet_api::fungibles` using the Substrate node and recommended hardware. +/// Weights for `fungibles` using the Substrate node and recommended hardware. pub struct SubstrateWeight(PhantomData); impl WeightInfo for SubstrateWeight { /// Storage: `Assets::Approvals` (r:1 w:1) @@ -49,18 +56,87 @@ impl WeightInfo for SubstrateWeight { fn approve(a: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `413 + c * (102 ±0)` - // Estimated: `3675 + c * (1797 ±0)` - // Minimum execution time: 35_000_000 picoseconds. - Weight::from_parts(1_207_482, 3675) - // Standard Error: 948_955 - .saturating_add(Weight::from_parts(34_649_659, 0).saturating_mul(a.into())) - // Standard Error: 948_955 - .saturating_add(Weight::from_parts(56_976_190, 0).saturating_mul(c.into())) + // Estimated: `3675` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(1_473_469, 3675) + // Standard Error: 89_329 + .saturating_add(Weight::from_parts(19_606_122, 0).saturating_mul(a.into())) + // Standard Error: 89_329 + .saturating_add(Weight::from_parts(30_920_408, 0).saturating_mul(c.into())) .saturating_add(T::DbWeight::get().reads(2_u64)) .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(T::DbWeight::get().writes(2_u64)) .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 1797).saturating_mul(c.into())) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + fn total_supply() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3675` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3675) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3599` + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(3_000_000, 3599) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Approvals` (r:1 w:0) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + fn allowance() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3613` + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 3613) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + fn token_name() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3605` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3605) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + fn token_symbol() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3605` + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 3605) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + fn token_decimals() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3605` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3605) + .saturating_add(T::DbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + fn token_exists() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3675` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3675) + .saturating_add(T::DbWeight::get().reads(1_u64)) } } @@ -77,18 +153,87 @@ impl WeightInfo for () { fn approve(a: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: // Measured: `413 + c * (102 ±0)` - // Estimated: `3675 + c * (1797 ±0)` - // Minimum execution time: 35_000_000 picoseconds. - Weight::from_parts(1_207_482, 3675) - // Standard Error: 948_955 - .saturating_add(Weight::from_parts(34_649_659, 0).saturating_mul(a.into())) - // Standard Error: 948_955 - .saturating_add(Weight::from_parts(56_976_190, 0).saturating_mul(c.into())) + // Estimated: `3675` + // Minimum execution time: 20_000_000 picoseconds. + Weight::from_parts(1_473_469, 3675) + // Standard Error: 89_329 + .saturating_add(Weight::from_parts(19_606_122, 0).saturating_mul(a.into())) + // Standard Error: 89_329 + .saturating_add(Weight::from_parts(30_920_408, 0).saturating_mul(c.into())) .saturating_add(RocksDbWeight::get().reads(2_u64)) .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) .saturating_add(RocksDbWeight::get().writes(2_u64)) .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) - .saturating_add(Weight::from_parts(0, 1797).saturating_mul(c.into())) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + fn total_supply() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3675` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3675) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Account` (r:1 w:0) + /// Proof: `Assets::Account` (`max_values`: None, `max_size`: Some(134), added: 2609, mode: `MaxEncodedLen`) + fn balance_of() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3599` + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(3_000_000, 3599) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Approvals` (r:1 w:0) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + fn allowance() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3613` + // Minimum execution time: 4_000_000 picoseconds. + Weight::from_parts(4_000_000, 3613) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + fn token_name() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3605` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3605) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + fn token_symbol() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3605` + // Minimum execution time: 2_000_000 picoseconds. + Weight::from_parts(2_000_000, 3605) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Metadata` (r:1 w:0) + /// Proof: `Assets::Metadata` (`max_values`: None, `max_size`: Some(140), added: 2615, mode: `MaxEncodedLen`) + fn token_decimals() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3605` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3605) + .saturating_add(RocksDbWeight::get().reads(1_u64)) + } + /// Storage: `Assets::Asset` (r:1 w:0) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + fn token_exists() -> Weight { + // Proof Size summary in bytes: + // Measured: `6` + // Estimated: `3675` + // Minimum execution time: 1_000_000 picoseconds. + Weight::from_parts(2_000_000, 3675) + .saturating_add(RocksDbWeight::get().reads(1_u64)) } } diff --git a/pop-api/examples/fungibles/lib.rs b/pop-api/examples/fungibles/lib.rs index 1ad36de0..11eafe21 100755 --- a/pop-api/examples/fungibles/lib.rs +++ b/pop-api/examples/fungibles/lib.rs @@ -1,14 +1,9 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -/// Local Fungibles: -/// 1. PSP-22 Interface -/// 2. PSP-22 Metadata Interface -/// 3. Asset Management -/// use ink::prelude::vec::Vec; use pop_api::{ - assets::fungibles::{self as api}, - primitives::AssetId, + fungibles::{self as api}, + primitives::TokenId, StatusCode, }; @@ -25,161 +20,89 @@ mod fungibles { impl Fungibles { #[ink(constructor, payable)] pub fn new() -> Self { - ink::env::debug_println!("PopApiFungiblesExample::new"); Default::default() } - /// 1. PSP-22 Interface: - /// - total_supply - /// - balance_of - /// - allowance - /// - transfer - /// - transfer_from - /// - approve - /// - increase_allowance - /// - decrease_allowance - #[ink(message)] - pub fn total_supply(&self, id: AssetId) -> Result { - api::total_supply(id) + pub fn total_supply(&self, token: TokenId) -> Result { + api::total_supply(token) } #[ink(message)] - pub fn balance_of(&self, id: AssetId, owner: AccountId) -> Result { - api::balance_of(id, owner) + pub fn balance_of(&self, token: TokenId, owner: AccountId) -> Result { + api::balance_of(token, owner) } #[ink(message)] pub fn allowance( &self, - id: AssetId, + token: TokenId, owner: AccountId, spender: AccountId, ) -> Result { - api::allowance(id, owner, spender) + api::allowance(token, owner, spender) } #[ink(message)] - pub fn transfer(&mut self, id: AssetId, to: AccountId, value: Balance) -> Result<()> { - api::transfer(id, to, value) + pub fn transfer(&mut self, token: TokenId, to: AccountId, value: Balance) -> Result<()> { + api::transfer(token, to, value) } #[ink(message)] pub fn transfer_from( &mut self, - id: AssetId, + token: TokenId, from: AccountId, to: AccountId, value: Balance, - // In the PSP-22 standard a `[u8]`, but the size needs to be known at compile time. _data: Vec, ) -> Result<()> { - api::transfer_from(id, from, to, value) + api::transfer_from(token, from, to, value) } #[ink(message)] - pub fn approve(&mut self, id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - api::approve(id, spender, value) + pub fn approve( + &mut self, + token: TokenId, + spender: AccountId, + value: Balance, + ) -> Result<()> { + api::approve(token, spender, value) } #[ink(message)] pub fn increase_allowance( &mut self, - id: AssetId, + token: TokenId, spender: AccountId, value: Balance, ) -> Result<()> { - api::increase_allowance(id, spender, value) + api::increase_allowance(token, spender, value) } #[ink(message)] pub fn decrease_allowance( &mut self, - id: AssetId, + token: TokenId, spender: AccountId, value: Balance, ) -> Result<()> { - api::decrease_allowance(id, spender, value) + api::decrease_allowance(token, spender, value) } - /// 2. PSP-22 Metadata Interface: - /// - token_name - /// - token_symbol - /// - token_decimals - #[ink(message)] - pub fn token_name(&self, id: AssetId) -> Result> { - api::token_name(id) + pub fn token_name(&self, token: TokenId) -> Result> { + api::token_name(token) } #[ink(message)] - pub fn token_symbol(&self, id: AssetId) -> Result> { - api::token_symbol(id) + pub fn token_symbol(&self, token: TokenId) -> Result> { + api::token_symbol(token) } #[ink(message)] - pub fn token_decimals(&self, id: AssetId) -> Result { - api::token_decimals(id) - } - - // 3. Asset Management: - // - create - // - start_destroy - // - destroy_accounts - // - destroy_approvals - // - finish_destroy - // - set_metadata - // - clear_metadata - - // #[ink(message)] - // pub fn create(&self, id: AssetId, admin: AccountId, min_balance: Balance) -> Result<()> { - // ink::env::debug_println!( - // "PopApiFungiblesExample::create: id: {:?} admin: {:?} min_balance: {:?}", - // id, - // admin, - // min_balance, - // ); - // let result = api::create(id, admin, min_balance); - // ink::env::debug_println!("Result: {:?}", result); - // result.map_err(|e| e.into()) - // result - // } - - // #[ink(message)] - // pub fn set_metadata( - // &self, - // id: AssetId, - // name: Vec, - // symbol: Vec, - // decimals: u8, - // ) -> Result<()> { - // ink::env::debug_println!( - // "PopApiFungiblesExample::set_metadata: id: {:?} name: {:?} symbol: {:?}, decimals: {:?}", - // id, - // name, - // symbol, - // decimals, - // ); - // let result = api::set_metadata(id, name, symbol, decimals); - // ink::env::debug_println!("Result: {:?}", result); - // // result.map_err(|e| e.into()) - // result - // } - // - // #[ink(message)] - // pub fn asset_exists(&self, id: AssetId) -> Result { - // // api::asset_exists(id).map_err(|e| e.into()) - // api::asset_exists(id) - // } - } - - #[cfg(test)] - mod tests { - use super::*; - - #[ink::test] - fn default_works() { - PopApiFungiblesExample::new(); + pub fn token_decimals(&self, token: TokenId) -> Result { + api::token_decimals(token) } } -} \ No newline at end of file +} diff --git a/pop-api/integration-tests/contracts/create_token_in_constructor/Cargo.toml b/pop-api/integration-tests/contracts/create_token_in_constructor/Cargo.toml index c3d2c2fc..a1517cdd 100755 --- a/pop-api/integration-tests/contracts/create_token_in_constructor/Cargo.toml +++ b/pop-api/integration-tests/contracts/create_token_in_constructor/Cargo.toml @@ -1,5 +1,4 @@ [package] -authors = [ "[your_name] <[your_email]>" ] edition = "2021" name = "create_token_in_constructor" version = "0.1.0" diff --git a/pop-api/integration-tests/contracts/fungibles/Cargo.toml b/pop-api/integration-tests/contracts/fungibles/Cargo.toml index 0d0e80d5..dce9b257 100755 --- a/pop-api/integration-tests/contracts/fungibles/Cargo.toml +++ b/pop-api/integration-tests/contracts/fungibles/Cargo.toml @@ -1,5 +1,4 @@ [package] -authors = [ "[your_name] <[your_email]>" ] edition = "2021" name = "fungibles" version = "0.1.0" diff --git a/pop-api/integration-tests/src/fungibles/mod.rs b/pop-api/integration-tests/src/fungibles/mod.rs index bfea0b45..346e67c6 100644 --- a/pop-api/integration-tests/src/fungibles/mod.rs +++ b/pop-api/integration-tests/src/fungibles/mod.rs @@ -1,4 +1,4 @@ -use pop_primitives::{ArithmeticError::*, Error::*, TokenError::*, TokenId, *}; +use pop_primitives::{ArithmeticError::*, Error, Error::*, TokenError::*, TokenId}; use utils::*; use super::*; @@ -21,102 +21,89 @@ const CONTRACT: &str = "contracts/fungibles/target/ink/fungibles.wasm"; #[test] fn total_supply_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. - assert_eq!(total_supply(addr.clone(), TOKEN_ID), Ok(Assets::total_supply(TOKEN_ID))); - assert_eq!(total_supply(addr.clone(), TOKEN_ID), Ok(0)); + assert_eq!(total_supply(&addr, TOKEN_ID), Ok(Assets::total_supply(TOKEN_ID))); + assert_eq!(total_supply(&addr, TOKEN_ID), Ok(0)); // Tokens in circulation. - create_asset_and_mint_to(addr.clone(), TOKEN_ID, BOB, 100); - assert_eq!(total_supply(addr.clone(), TOKEN_ID), Ok(Assets::total_supply(TOKEN_ID))); - assert_eq!(total_supply(addr, TOKEN_ID), Ok(100)); + assets::create_and_mint_to(&addr, TOKEN_ID, &BOB, 100); + assert_eq!(total_supply(&addr, TOKEN_ID), Ok(Assets::total_supply(TOKEN_ID))); + assert_eq!(total_supply(&addr, TOKEN_ID), Ok(100)); }); } #[test] fn balance_of_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. - assert_eq!(balance_of(addr.clone(), TOKEN_ID, BOB), Ok(Assets::balance(TOKEN_ID, BOB))); - assert_eq!(balance_of(addr.clone(), TOKEN_ID, BOB), Ok(0)); + assert_eq!(balance_of(&addr, TOKEN_ID, BOB), Ok(Assets::balance(TOKEN_ID, BOB))); + assert_eq!(balance_of(&addr, TOKEN_ID, BOB), Ok(0)); // Tokens in circulation. - create_asset_and_mint_to(addr.clone(), TOKEN_ID, BOB, 100); - assert_eq!(balance_of(addr.clone(), TOKEN_ID, BOB), Ok(Assets::balance(TOKEN_ID, BOB))); - assert_eq!(balance_of(addr, TOKEN_ID, BOB), Ok(100)); + assets::create_and_mint_to(&addr, TOKEN_ID, &BOB, 100); + assert_eq!(balance_of(&addr, TOKEN_ID, BOB), Ok(Assets::balance(TOKEN_ID, BOB))); + assert_eq!(balance_of(&addr, TOKEN_ID, BOB), Ok(100)); }); } #[test] fn allowance_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!( - allowance(addr.clone(), TOKEN_ID, BOB, ALICE), + allowance(&addr, TOKEN_ID, BOB, ALICE), Ok(Assets::allowance(TOKEN_ID, &BOB, &ALICE)) ); - assert_eq!(allowance(addr.clone(), TOKEN_ID, BOB, ALICE), Ok(0)); + assert_eq!(allowance(&addr, TOKEN_ID, BOB, ALICE), Ok(0)); // Tokens in circulation. - create_asset_mint_and_approve(addr.clone(), TOKEN_ID, BOB, 100, ALICE, 50); + assets::create_mint_and_approve(&addr, TOKEN_ID, &BOB, 100, &ALICE, 50); assert_eq!( - allowance(addr.clone(), TOKEN_ID, BOB, ALICE), + allowance(&addr, TOKEN_ID, BOB, ALICE), Ok(Assets::allowance(TOKEN_ID, &BOB, &ALICE)) ); - assert_eq!(allowance(addr, TOKEN_ID, BOB, ALICE), Ok(50)); + assert_eq!(allowance(&addr, TOKEN_ID, BOB, ALICE), Ok(50)); }); } #[test] fn transfer_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. - assert_eq!( - transfer(addr.clone(), 1, BOB, amount), - Err(Module { index: 52, error: [3, 0] }) - ); - // Create asset with Alice as owner and mint `amount` to contract address. - let asset = create_asset_and_mint_to(ALICE, 1, addr.clone(), amount); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(ALICE, asset); - assert_eq!( - transfer(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [16, 0] }) - ); - thaw_asset(ALICE, asset); + // Token does not exist. + assert_eq!(transfer(&addr, 1, BOB, amount), Err(Module { index: 52, error: [3, 0] })); + // Create token with Alice as owner and mint `amount` to contract address. + let token = assets::create_and_mint_to(&ALICE, 1, &addr, amount); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&ALICE, token); + assert_eq!(transfer(&addr, token, BOB, amount), Err(Module { index: 52, error: [16, 0] })); + assets::thaw(&ALICE, token); // Not enough balance. assert_eq!( - transfer(addr.clone(), asset, BOB, amount + 1 * UNIT), + transfer(&addr, token, BOB, amount + 1 * UNIT), Err(Module { index: 52, error: [0, 0] }) ); // Not enough balance due to ED. - assert_eq!( - transfer(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [0, 0] }) - ); + assert_eq!(transfer(&addr, token, BOB, amount), Err(Module { index: 52, error: [0, 0] })); // Successful transfer. - let balance_before_transfer = Assets::balance(asset, &BOB); - assert_ok!(transfer(addr.clone(), asset, BOB, amount / 2)); - let balance_after_transfer = Assets::balance(asset, &BOB); + let balance_before_transfer = Assets::balance(token, &BOB); + assert_ok!(transfer(&addr, token, BOB, amount / 2)); + let balance_after_transfer = Assets::balance(token, &BOB); assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); - // Transfer asset to account that does not exist. - assert_eq!(transfer(addr.clone(), asset, FERDIE, amount / 4), Err(Token(CannotCreate))); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(ALICE, asset); + // Transfer token to account that does not exist. + assert_eq!(transfer(&addr, token, FERDIE, amount / 4), Err(Token(CannotCreate))); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&ALICE, token); assert_eq!( - transfer(addr.clone(), asset, BOB, amount / 4), + transfer(&addr, token, BOB, amount / 4), Err(Module { index: 52, error: [16, 0] }) ); }); @@ -125,44 +112,43 @@ fn transfer_works() { #[test] fn transfer_from_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. + // Token does not exist. assert_eq!( - transfer_from(addr.clone(), 1, ALICE, BOB, amount / 2), + transfer_from(&addr, 1, ALICE, BOB, amount / 2), Err(Module { index: 52, error: [3, 0] }), ); - // Create asset with Alice as owner and mint `amount` to contract address. - let asset = create_asset_and_mint_to(ALICE, 1, ALICE, amount); + // Create token with Alice as owner and mint `amount` to contract address. + let token = assets::create_and_mint_to(&ALICE, 1, &ALICE, amount); // Unapproved transfer. assert_eq!( - transfer_from(addr.clone(), asset, ALICE, BOB, amount / 2), + transfer_from(&addr, token, ALICE, BOB, amount / 2), Err(Module { index: 52, error: [10, 0] }) ); assert_ok!(Assets::approve_transfer( RuntimeOrigin::signed(ALICE.into()), - asset.into(), + token.into(), addr.clone().into(), amount + 1 * UNIT, )); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(ALICE, asset); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&ALICE, token); assert_eq!( - transfer_from(addr.clone(), asset, ALICE, BOB, amount), + transfer_from(&addr, token, ALICE, BOB, amount), Err(Module { index: 52, error: [16, 0] }), ); - thaw_asset(ALICE, asset); + assets::thaw(&ALICE, token); // Not enough balance. assert_eq!( - transfer_from(addr.clone(), asset, ALICE, BOB, amount + 1 * UNIT), + transfer_from(&addr, token, ALICE, BOB, amount + 1 * UNIT), Err(Module { index: 52, error: [0, 0] }), ); // Successful transfer. - let balance_before_transfer = Assets::balance(asset, &BOB); - assert_ok!(transfer_from(addr.clone(), asset, ALICE, BOB, amount / 2)); - let balance_after_transfer = Assets::balance(asset, &BOB); + let balance_before_transfer = Assets::balance(token, &BOB); + assert_ok!(transfer_from(&addr, token, ALICE, BOB, amount / 2)); + let balance_after_transfer = Assets::balance(token, &BOB); assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); }); } @@ -170,77 +156,69 @@ fn transfer_from_works() { #[test] fn approve_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, 0, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. - assert_eq!(approve(addr.clone(), 0, BOB, amount), Err(Module { index: 52, error: [3, 0] })); - let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); - assert_eq!(approve(addr.clone(), asset, BOB, amount), Err(ConsumerRemaining)); + // Token does not exist. + assert_eq!(approve(&addr, 0, &BOB, amount), Err(Module { index: 52, error: [3, 0] })); + let token = assets::create_and_mint_to(&ALICE, 0, &addr, amount); + assert_eq!(approve(&addr, token, &BOB, amount), Err(ConsumerRemaining)); let addr = instantiate(CONTRACT, INIT_VALUE, vec![1]); - // Create asset with Alice as owner and mint `amount` to contract address. - let asset = create_asset_and_mint_to(ALICE, 1, addr.clone(), amount); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(ALICE, asset); - assert_eq!( - approve(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [16, 0] }) - ); - thaw_asset(ALICE, asset); + // Create token with Alice as owner and mint `amount` to contract address. + let token = assets::create_and_mint_to(&ALICE, 1, &addr, amount); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&ALICE, token); + assert_eq!(approve(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] })); + assets::thaw(&ALICE, token); // Successful approvals: - assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); - assert_ok!(approve(addr.clone(), asset, BOB, amount)); - assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); + assert_eq!(0, Assets::allowance(token, &addr, &BOB)); + assert_ok!(approve(&addr, token, &BOB, amount)); + assert_eq!(Assets::allowance(token, &addr, &BOB), amount); // Non-additive, sets new value. - assert_ok!(approve(addr.clone(), asset, BOB, amount / 2)); - assert_eq!(Assets::allowance(asset, &addr, &BOB), amount / 2); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(ALICE, asset); - assert_eq!( - approve(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [16, 0] }) - ); + assert_ok!(approve(&addr, token, &BOB, amount / 2)); + assert_eq!(Assets::allowance(token, &addr, &BOB), amount / 2); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&ALICE, token); + assert_eq!(approve(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] })); }); } #[test] fn increase_allowance_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let amount: Balance = 100 * UNIT; // Instantiate a contract without balance - test `ConsumerRemaining. let addr = instantiate(CONTRACT, 0, vec![]); - // Asset does not exist. + // Token does not exist. assert_eq!( - increase_allowance(addr.clone(), 0, BOB, amount), + increase_allowance(&addr, 0, &BOB, amount), Err(Module { index: 52, error: [3, 0] }) ); - let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); - assert_eq!(increase_allowance(addr.clone(), asset, BOB, amount), Err(ConsumerRemaining)); + let token = assets::create_and_mint_to(&ALICE, 0, &addr, amount); + assert_eq!(increase_allowance(&addr, token, &BOB, amount), Err(ConsumerRemaining)); // Instantiate a contract with balance. let addr = instantiate(CONTRACT, INIT_VALUE, vec![1]); - // Create asset with Alice as owner and mint `amount` to contract address. - let asset = create_asset_and_mint_to(ALICE, 1, addr.clone(), amount); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(ALICE, asset); + // Create token with Alice as owner and mint `amount` to contract address. + let token = assets::create_and_mint_to(&ALICE, 1, &addr, amount); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&ALICE, token); assert_eq!( - increase_allowance(addr.clone(), asset, BOB, amount), + increase_allowance(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] }) ); - thaw_asset(ALICE, asset); + assets::thaw(&ALICE, token); // Successful approvals: - assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); - assert_ok!(increase_allowance(addr.clone(), asset, BOB, amount)); - assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); + assert_eq!(0, Assets::allowance(token, &addr, &BOB)); + assert_ok!(increase_allowance(&addr, token, &BOB, amount)); + assert_eq!(Assets::allowance(token, &addr, &BOB), amount); // Additive. - assert_ok!(increase_allowance(addr.clone(), asset, BOB, amount)); - assert_eq!(Assets::allowance(asset, &addr, &BOB), amount * 2); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(ALICE, asset); + assert_ok!(increase_allowance(&addr, token, &BOB, amount)); + assert_eq!(Assets::allowance(token, &addr, &BOB), amount * 2); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&ALICE, token); assert_eq!( - increase_allowance(addr.clone(), asset, BOB, amount), + increase_allowance(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] }) ); }); @@ -249,34 +227,32 @@ fn increase_allowance_works() { #[test] fn decrease_allowance_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. + // Token does not exist. assert_eq!( - decrease_allowance(addr.clone(), 0, BOB, amount), + decrease_allowance(&addr, 0, &BOB, amount), Err(Module { index: 52, error: [3, 0] }), ); - // Create asset and mint `amount` to contract address, then approve Bob to spend `amount`. - let asset = - create_asset_mint_and_approve(addr.clone(), 0, addr.clone(), amount, BOB, amount); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(addr.clone(), asset); + // Create token and mint `amount` to contract address, then approve Bob to spend `amount`. + let token = assets::create_mint_and_approve(&addr, 0, &addr, amount, &BOB, amount); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&addr, token); assert_eq!( - decrease_allowance(addr.clone(), asset, BOB, amount), + decrease_allowance(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] }), ); - thaw_asset(addr.clone(), asset); + assets::thaw(&addr, token); // Successfully decrease allowance. - let allowance_before = Assets::allowance(asset, &addr, &BOB); - assert_ok!(decrease_allowance(addr.clone(), 0, BOB, amount / 2 - 1 * UNIT)); - let allowance_after = Assets::allowance(asset, &addr, &BOB); + let allowance_before = Assets::allowance(token, &addr, &BOB); + assert_ok!(decrease_allowance(&addr, 0, &BOB, amount / 2 - 1 * UNIT)); + let allowance_after = Assets::allowance(token, &addr, &BOB); assert_eq!(allowance_before - allowance_after, amount / 2 - 1 * UNIT); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(addr.clone(), asset); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&addr, token); assert_eq!( - decrease_allowance(addr.clone(), asset, BOB, amount), + decrease_allowance(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] }), ); }); @@ -290,37 +266,29 @@ fn decrease_allowance_works() { #[test] fn token_metadata_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let name: Vec = vec![11, 12, 13]; let symbol: Vec = vec![21, 22, 23]; let decimals: u8 = 69; // Token does not exist. - assert_eq!(token_name(addr.clone(), TOKEN_ID), Ok(token_name_asset(TOKEN_ID))); - assert_eq!(token_name(addr.clone(), TOKEN_ID), Ok(Vec::::new())); - assert_eq!(token_symbol(addr.clone(), TOKEN_ID), Ok(token_symbol_asset(TOKEN_ID))); - assert_eq!(token_symbol(addr.clone(), TOKEN_ID), Ok(Vec::::new())); - assert_eq!(token_decimals(addr.clone(), TOKEN_ID), Ok(token_decimals_asset(TOKEN_ID))); - assert_eq!(token_decimals(addr.clone(), TOKEN_ID), Ok(0)); + assert_eq!(token_name(&addr, TOKEN_ID), Ok(Assets::name(TOKEN_ID))); + assert_eq!(token_name(&addr, TOKEN_ID), Ok(Vec::::new())); + assert_eq!(token_symbol(&addr, TOKEN_ID), Ok(Assets::symbol(TOKEN_ID))); + assert_eq!(token_symbol(&addr, TOKEN_ID), Ok(Vec::::new())); + assert_eq!(token_decimals(&addr, TOKEN_ID), Ok(Assets::decimals(TOKEN_ID))); + assert_eq!(token_decimals(&addr, TOKEN_ID), Ok(0)); // Create Token. - create_asset_and_set_metadata( - addr.clone(), - TOKEN_ID, - name.clone(), - symbol.clone(), - decimals, - ); - assert_eq!(token_name(addr.clone(), TOKEN_ID), Ok(token_name_asset(TOKEN_ID))); - assert_eq!(token_name(addr.clone(), TOKEN_ID), Ok(name)); - assert_eq!(token_symbol(addr.clone(), TOKEN_ID), Ok(token_symbol_asset(TOKEN_ID))); - assert_eq!(token_symbol(addr.clone(), TOKEN_ID), Ok(symbol)); - assert_eq!(token_decimals(addr.clone(), TOKEN_ID), Ok(token_decimals_asset(TOKEN_ID))); - assert_eq!(token_decimals(addr.clone(), TOKEN_ID), Ok(decimals)); + assets::create_and_set_metadata(&addr, TOKEN_ID, name.clone(), symbol.clone(), decimals); + assert_eq!(token_name(&addr, TOKEN_ID), Ok(Assets::name(TOKEN_ID))); + assert_eq!(token_name(&addr, TOKEN_ID), Ok(name)); + assert_eq!(token_symbol(&addr, TOKEN_ID), Ok(Assets::symbol(TOKEN_ID))); + assert_eq!(token_symbol(&addr, TOKEN_ID), Ok(symbol)); + assert_eq!(token_decimals(&addr, TOKEN_ID), Ok(Assets::decimals(TOKEN_ID))); + assert_eq!(token_decimals(&addr, TOKEN_ID), Ok(decimals)); }); } - -/// 3. Asset Management: +/// 3. Management: /// - create /// - start_destroy /// - set_metadata @@ -330,59 +298,45 @@ fn token_metadata_works() { #[test] fn create_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); // Instantiate a contract without balance for fees. let addr = instantiate(CONTRACT, 0, vec![0]); // No balance to pay for fees. - assert_eq!( - create(addr.clone(), TOKEN_ID, addr.clone(), 1), - Err(Module { index: 10, error: [2, 0] }), - ); + assert_eq!(create(&addr, TOKEN_ID, &addr, 1), Err(Module { index: 10, error: [2, 0] }),); // Instantiate a contract without balance for deposit. let addr = instantiate(CONTRACT, 100, vec![1]); // No balance to pay the deposit. - assert_eq!( - create(addr.clone(), TOKEN_ID, addr.clone(), 1), - Err(Module { index: 10, error: [2, 0] }), - ); + assert_eq!(create(&addr, TOKEN_ID, &addr, 1), Err(Module { index: 10, error: [2, 0] }),); // Instantiate a contract with enough balance. let addr = instantiate(CONTRACT, INIT_VALUE, vec![2]); - assert_eq!( - create(addr.clone(), TOKEN_ID, BOB, 0), - Err(Module { index: 52, error: [7, 0] }), - ); - // The minimal balance for an asset must be non zero. - assert_eq!( - create(addr.clone(), TOKEN_ID, BOB, 0), - Err(Module { index: 52, error: [7, 0] }), - ); - // Create asset successfully. - assert_ok!(create(addr.clone(), TOKEN_ID, BOB, 1)); - // Asset ID is already taken. - assert_eq!( - create(addr.clone(), TOKEN_ID, BOB, 1), - Err(Module { index: 52, error: [5, 0] }), - ); + assert_eq!(create(&addr, TOKEN_ID, &BOB, 0), Err(Module { index: 52, error: [7, 0] }),); + // The minimal balance for a token must be non zero. + assert_eq!(create(&addr, TOKEN_ID, &BOB, 0), Err(Module { index: 52, error: [7, 0] }),); + // Create token successfully. + assert_ok!(create(&addr, TOKEN_ID, &BOB, 1)); + assert_eq!(Assets::owner(TOKEN_ID), Some(addr.clone())); + // Token ID is already taken. + assert_eq!(create(&addr, TOKEN_ID, &BOB, 1), Err(Module { index: 52, error: [5, 0] }),); }); } -// Testing a contract that creates an asset in the constructor. +// Testing a contract that creates a token in the constructor. #[test] fn instantiate_and_create_fungible_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let contract = "contracts/create_token_in_constructor/target/ink/create_token_in_constructor.wasm"; - // Asset already exists. - create_asset(ALICE, 0, 1); + // Token already exists. + assets::create(&ALICE, 0, 1); assert_eq!( instantiate_and_create_fungible(contract, 0, 1), Err(Module { index: 52, error: [5, 0] }) ); - // Successfully create an asset when instantiating the contract. - assert_ok!(instantiate_and_create_fungible(contract, TOKEN_ID, 1)); + // Successfully create a token when instantiating the contract. + let result_with_address = instantiate_and_create_fungible(contract, TOKEN_ID, 1); + assert_ok!(result_with_address.clone()); + assert_eq!(Assets::owner(TOKEN_ID), result_with_address.ok()); assert!(Assets::asset_exists(TOKEN_ID)); }); } @@ -390,62 +344,60 @@ fn instantiate_and_create_fungible_works() { #[test] fn start_destroy_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![2]); - // Asset does not exist. - assert_eq!(start_destroy(addr.clone(), TOKEN_ID), Err(Module { index: 52, error: [3, 0] }),); - // Create assets where contract is not the owner. - let asset = create_asset(ALICE, 0, 1); + // Token does not exist. + assert_eq!(start_destroy(&addr, TOKEN_ID), Err(Module { index: 52, error: [3, 0] }),); + // Create tokens where contract is not the owner. + let token = assets::create(&ALICE, 0, 1); // No Permission. - assert_eq!(start_destroy(addr.clone(), asset), Err(Module { index: 52, error: [2, 0] }),); - let asset = create_asset(addr.clone(), TOKEN_ID, 1); - assert_ok!(start_destroy(addr.clone(), asset)); + assert_eq!(start_destroy(&addr, token), Err(Module { index: 52, error: [2, 0] }),); + let token = assets::create(&addr, TOKEN_ID, 1); + assert_ok!(start_destroy(&addr, token)); }); } #[test] fn set_metadata_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let name = vec![42]; let symbol = vec![42]; let decimals = 42u8; let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); - // Asset does not exist. + // Token does not exist. assert_eq!( - set_metadata(addr.clone(), TOKEN_ID, vec![0], vec![0], 0u8), + set_metadata(&addr, TOKEN_ID, vec![0], vec![0], 0u8), Err(Module { index: 52, error: [3, 0] }), ); - // Create assets where contract is not the owner. - let asset = create_asset(ALICE, 0, 1); + // Create token where contract is not the owner. + let token = assets::create(&ALICE, 0, 1); // No Permission. assert_eq!( - set_metadata(addr.clone(), asset, vec![0], vec![0], 0u8), + set_metadata(&addr, token, vec![0], vec![0], 0u8), Err(Module { index: 52, error: [2, 0] }), ); - let asset = create_asset(addr.clone(), TOKEN_ID, 1); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(addr.clone(), asset); + let token = assets::create(&addr, TOKEN_ID, 1); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&addr, token); assert_eq!( - set_metadata(addr.clone(), TOKEN_ID, vec![0], vec![0], 0u8), + set_metadata(&addr, TOKEN_ID, vec![0], vec![0], 0u8), Err(Module { index: 52, error: [16, 0] }), ); - thaw_asset(addr.clone(), asset); + assets::thaw(&addr, token); // TODO: calling the below with a vector of length `100_000` errors in pallet contracts // `OutputBufferTooSmall. Added to security analysis issue #131 to revisit. // Set bad metadata - too large values. assert_eq!( - set_metadata(addr.clone(), TOKEN_ID, vec![0; 1000], vec![0; 1000], 0u8), + set_metadata(&addr, TOKEN_ID, vec![0; 1000], vec![0; 1000], 0u8), Err(Module { index: 52, error: [9, 0] }), ); // Set metadata successfully. - assert_ok!(set_metadata(addr.clone(), TOKEN_ID, name, symbol, decimals)); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(addr.clone(), asset); + assert_ok!(set_metadata(&addr, TOKEN_ID, name, symbol, decimals)); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&addr, token); assert_eq!( - set_metadata(addr.clone(), TOKEN_ID, vec![0], vec![0], 0), + set_metadata(&addr, TOKEN_ID, vec![0], vec![0], 0), Err(Module { index: 52, error: [16, 0] }), ); }); @@ -454,32 +406,31 @@ fn set_metadata_works() { #[test] fn clear_metadata_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let name = vec![42]; let symbol = vec![42]; let decimals = 42u8; let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); - // Asset does not exist. - assert_eq!(clear_metadata(addr.clone(), 0), Err(Module { index: 52, error: [3, 0] }),); - // Create assets where contract is not the owner. - let asset = create_asset_and_set_metadata(ALICE, 0, vec![0], vec![0], 0); + // Token does not exist. + assert_eq!(clear_metadata(&addr, 0), Err(Module { index: 52, error: [3, 0] }),); + // Create token where contract is not the owner. + let token = assets::create_and_set_metadata(&ALICE, 0, vec![0], vec![0], 0); // No Permission. - assert_eq!(clear_metadata(addr.clone(), asset), Err(Module { index: 52, error: [2, 0] }),); - let asset = create_asset(addr.clone(), TOKEN_ID, 1); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(addr.clone(), asset); - assert_eq!(clear_metadata(addr.clone(), asset), Err(Module { index: 52, error: [16, 0] }),); - thaw_asset(addr.clone(), asset); + assert_eq!(clear_metadata(&addr, token), Err(Module { index: 52, error: [2, 0] }),); + let token = assets::create(&addr, TOKEN_ID, 1); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&addr, token); + assert_eq!(clear_metadata(&addr, token), Err(Module { index: 52, error: [16, 0] }),); + assets::thaw(&addr, token); // No metadata set. - assert_eq!(clear_metadata(addr.clone(), asset), Err(Module { index: 52, error: [3, 0] }),); - set_metadata_asset(addr.clone(), asset, name, symbol, decimals); + assert_eq!(clear_metadata(&addr, token), Err(Module { index: 52, error: [3, 0] }),); + assets::set_metadata(&addr, token, name, symbol, decimals); // Clear metadata successfully. - assert_ok!(clear_metadata(addr.clone(), TOKEN_ID)); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(addr.clone(), asset); + assert_ok!(clear_metadata(&addr, TOKEN_ID)); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&addr, token); assert_eq!( - set_metadata(addr.clone(), TOKEN_ID, vec![0], vec![0], decimals), + set_metadata(&addr, TOKEN_ID, vec![0], vec![0], decimals), Err(Module { index: 52, error: [16, 0] }), ); }); @@ -488,89 +439,74 @@ fn clear_metadata_works() { #[test] fn token_exists_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. - assert_eq!(token_exists(addr.clone(), TOKEN_ID), Ok(Assets::asset_exists(TOKEN_ID))); + assert_eq!(token_exists(&addr, TOKEN_ID), Ok(Assets::asset_exists(TOKEN_ID))); // Tokens in circulation. - create_asset(addr.clone(), TOKEN_ID, 1); - assert_eq!(token_exists(addr.clone(), TOKEN_ID), Ok(Assets::asset_exists(TOKEN_ID))); + assets::create(&addr, TOKEN_ID, 1); + assert_eq!(token_exists(&addr, TOKEN_ID), Ok(Assets::asset_exists(TOKEN_ID))); }); } #[test] fn mint_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. - assert_eq!(mint(addr.clone(), 1, BOB, amount), Err(Token(UnknownAsset))); - let asset = create_asset(ALICE, 1, 1); + // Token does not exist. + assert_eq!(mint(&addr, 1, &BOB, amount), Err(Token(UnknownAsset))); + let token = assets::create(&ALICE, 1, 1); // Minting can only be done by the owner. - assert_eq!(mint(addr.clone(), asset, BOB, 1), Err(Module { index: 52, error: [2, 0] })); - let asset = create_asset(addr.clone(), 2, 2); - // Minimum balance of an asset can not be zero. - assert_eq!(mint(addr.clone(), asset, BOB, 1), Err(Token(BelowMinimum))); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(addr.clone(), asset); - assert_eq!( - mint(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [16, 0] }) - ); - thaw_asset(addr.clone(), asset); + assert_eq!(mint(&addr, token, &BOB, 1), Err(Module { index: 52, error: [2, 0] })); + let token = assets::create(&addr, 2, 2); + // Minimum balance of a token can not be zero. + assert_eq!(mint(&addr, token, &BOB, 1), Err(Token(BelowMinimum))); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&addr, token); + assert_eq!(mint(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] })); + assets::thaw(&addr, token); // Successful mint. - let balance_before_mint = Assets::balance(asset, &BOB); - assert_ok!(mint(addr.clone(), asset, BOB, amount)); - let balance_after_mint = Assets::balance(asset, &BOB); + let balance_before_mint = Assets::balance(token, &BOB); + assert_ok!(mint(&addr, token, &BOB, amount)); + let balance_after_mint = Assets::balance(token, &BOB); assert_eq!(balance_after_mint, balance_before_mint + amount); // Account can not hold more tokens than Balance::MAX. - assert_eq!(mint(addr.clone(), asset, BOB, Balance::MAX,), Err(Arithmetic(Overflow))); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(addr.clone(), asset); - assert_eq!( - mint(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [16, 0] }) - ); + assert_eq!(mint(&addr, token, &BOB, Balance::MAX,), Err(Arithmetic(Overflow))); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&addr, token); + assert_eq!(mint(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] })); }); } #[test] fn burn_works() { new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. - assert_eq!(burn(addr.clone(), 1, BOB, amount), Err(Module { index: 52, error: [3, 0] })); - let asset = create_asset(ALICE, 1, 1); - // Bob has no tokens and thus pallet assets doesn't know the account. - assert_eq!(burn(addr.clone(), asset, BOB, 1), Err(Module { index: 52, error: [1, 0] })); + // Token does not exist. + assert_eq!(burn(&addr, 1, &BOB, amount), Err(Module { index: 52, error: [3, 0] })); + let token = assets::create(&ALICE, 1, 1); + // Bob has no tokens and therefore doesn't exist. + assert_eq!(burn(&addr, token, &BOB, 1), Err(Module { index: 52, error: [1, 0] })); // Burning can only be done by the manager. - mint_asset(ALICE, asset, BOB, amount); - assert_eq!(burn(addr.clone(), asset, BOB, 1), Err(Module { index: 52, error: [2, 0] })); - let asset = create_asset_and_mint_to(addr.clone(), 2, BOB, amount); - // Asset is not live, i.e. frozen or being destroyed. - freeze_asset(addr.clone(), asset); - assert_eq!( - burn(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [16, 0] }) - ); - thaw_asset(addr.clone(), asset); + assets::mint(&ALICE, token, &BOB, amount); + assert_eq!(burn(&addr, token, &BOB, 1), Err(Module { index: 52, error: [2, 0] })); + let token = assets::create_and_mint_to(&addr, 2, &BOB, amount); + // Token is not live, i.e. frozen or being destroyed. + assets::freeze(&addr, token); + assert_eq!(burn(&addr, token, &BOB, amount), Err(Module { index: 52, error: [16, 0] })); + assets::thaw(&addr, token); // Successful mint. - let balance_before_burn = Assets::balance(asset, &BOB); - assert_ok!(burn(addr.clone(), asset, BOB, amount)); - let balance_after_burn = Assets::balance(asset, &BOB); + let balance_before_burn = Assets::balance(token, &BOB); + assert_ok!(burn(&addr, token, &BOB, amount)); + let balance_after_burn = Assets::balance(token, &BOB); assert_eq!(balance_after_burn, balance_before_burn - amount); - // Asset is not live, i.e. frozen or being destroyed. - start_destroy_asset(addr.clone(), asset); - assert_eq!( - burn(addr.clone(), asset, BOB, amount), - Err(Module { index: 52, error: [17, 0] }) - ); + // Token is not live, i.e. frozen or being destroyed. + assets::start_destroy(&addr, token); + assert_eq!(burn(&addr, token, &BOB, amount), Err(Module { index: 52, error: [17, 0] })); }); } diff --git a/pop-api/integration-tests/src/fungibles/utils.rs b/pop-api/integration-tests/src/fungibles/utils.rs index 35761673..9b1fa984 100644 --- a/pop-api/integration-tests/src/fungibles/utils.rs +++ b/pop-api/integration-tests/src/fungibles/utils.rs @@ -1,145 +1,146 @@ use super::*; -fn do_bare_call(function: &str, addr: AccountId32, params: Vec) -> ExecReturnValue { +fn do_bare_call(function: &str, addr: &AccountId32, params: Vec) -> ExecReturnValue { let function = function_selector(function); let params = [function, params].concat(); - bare_call(addr, params, 0).expect("should work") + bare_call(addr.clone(), params, 0).expect("should work") } +// TODO - issue #263 - why result.data[1..] pub(super) fn decoded(result: ExecReturnValue) -> Result { ::decode(&mut &result.data[1..]).map_err(|_| result) } -pub(super) fn total_supply(addr: AccountId32, asset_id: AssetId) -> Result { - let result = do_bare_call("total_supply", addr, asset_id.encode()); +pub(super) fn total_supply(addr: &AccountId32, token_id: TokenId) -> Result { + let result = do_bare_call("total_supply", addr, token_id.encode()); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn balance_of( - addr: AccountId32, - asset_id: AssetId, + addr: &AccountId32, + token_id: TokenId, owner: AccountId32, ) -> Result { - let params = [asset_id.encode(), owner.encode()].concat(); - let result = do_bare_call("balance_of", addr, params); + let params = [token_id.encode(), owner.encode()].concat(); + let result = do_bare_call("balance_of", &addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn allowance( - addr: AccountId32, - asset_id: AssetId, + addr: &AccountId32, + token_id: TokenId, owner: AccountId32, spender: AccountId32, ) -> Result { - let params = [asset_id.encode(), owner.encode(), spender.encode()].concat(); - let result = do_bare_call("allowance", addr, params); + let params = [token_id.encode(), owner.encode(), spender.encode()].concat(); + let result = do_bare_call("allowance", &addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { - let result = do_bare_call("token_name", addr, asset_id.encode()); +pub(super) fn token_name(addr: &AccountId32, token_id: TokenId) -> Result, Error> { + let result = do_bare_call("token_name", addr, token_id.encode()); decoded::, Error>>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> { - let result = do_bare_call("token_symbol", addr, asset_id.encode()); +pub(super) fn token_symbol(addr: &AccountId32, token_id: TokenId) -> Result, Error> { + let result = do_bare_call("token_symbol", addr, token_id.encode()); decoded::, Error>>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { - let result = do_bare_call("token_decimals", addr, asset_id.encode()); +pub(super) fn token_decimals(addr: &AccountId32, token_id: TokenId) -> Result { + let result = do_bare_call("token_decimals", addr, token_id.encode()); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn token_exists(addr: AccountId32, asset_id: AssetId) -> Result { - let result = do_bare_call("token_exists", addr, asset_id.encode()); +pub(super) fn token_exists(addr: &AccountId32, token_id: TokenId) -> Result { + let result = do_bare_call("token_exists", addr, token_id.encode()); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn transfer( - addr: AccountId32, - asset_id: AssetId, + addr: &AccountId32, + token_id: TokenId, to: AccountId32, value: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), to.encode(), value.encode()].concat(); + let params = [token_id.encode(), to.encode(), value.encode()].concat(); let result = do_bare_call("transfer", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn transfer_from( - addr: AccountId32, - asset_id: AssetId, + addr: &AccountId32, + token_id: TokenId, from: AccountId32, to: AccountId32, value: Balance, ) -> Result<(), Error> { let data: Vec = vec![]; let params = - [asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()].concat(); + [token_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()].concat(); let result = do_bare_call("transfer_from", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn approve( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, + addr: &AccountId32, + token_id: TokenId, + spender: &AccountId32, value: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), spender.encode(), value.encode()].concat(); + let params = [token_id.encode(), spender.encode(), value.encode()].concat(); let result = do_bare_call("approve", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn increase_allowance( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, + addr: &AccountId32, + token_id: TokenId, + spender: &AccountId32, value: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), spender.encode(), value.encode()].concat(); + let params = [token_id.encode(), spender.encode(), value.encode()].concat(); let result = do_bare_call("increase_allowance", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn decrease_allowance( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, + addr: &AccountId32, + token_id: TokenId, + spender: &AccountId32, value: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), spender.encode(), value.encode()].concat(); + let params = [token_id.encode(), spender.encode(), value.encode()].concat(); let result = do_bare_call("decrease_allowance", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn create( - addr: AccountId32, - asset_id: AssetId, - admin: AccountId32, + addr: &AccountId32, + token_id: TokenId, + admin: &AccountId32, min_balance: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), admin.encode(), min_balance.encode()].concat(); + let params = [token_id.encode(), admin.encode(), min_balance.encode()].concat(); let result = do_bare_call("create", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn start_destroy(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { - let result = do_bare_call("start_destroy", addr, asset_id.encode()); +pub(super) fn start_destroy(addr: &AccountId32, token_id: TokenId) -> Result<(), Error> { + let result = do_bare_call("start_destroy", addr, token_id.encode()); match decoded::>(result) { Ok(x) => x, Err(result) => panic!("Contract reverted: {:?}", result), @@ -147,179 +148,176 @@ pub(super) fn start_destroy(addr: AccountId32, asset_id: AssetId) -> Result<(), } pub(super) fn set_metadata( - addr: AccountId32, - asset_id: AssetId, + addr: &AccountId32, + token_id: TokenId, name: Vec, symbol: Vec, decimals: u8, ) -> Result<(), Error> { - let params = [asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); + let params = [token_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); let result = do_bare_call("set_metadata", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn clear_metadata(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { - let result = do_bare_call("clear_metadata", addr, asset_id.encode()); +pub(super) fn clear_metadata(addr: &AccountId32, token_id: TokenId) -> Result<(), Error> { + let result = do_bare_call("clear_metadata", addr, token_id.encode()); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn mint( - addr: AccountId32, - asset_id: AssetId, - account: AccountId32, + addr: &AccountId32, + token_id: TokenId, + account: &AccountId32, amount: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), account.encode(), amount.encode()].concat(); + let params = [token_id.encode(), account.encode(), amount.encode()].concat(); let result = do_bare_call("mint", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } pub(super) fn burn( - addr: AccountId32, - asset_id: AssetId, - account: AccountId32, + addr: &AccountId32, + token_id: TokenId, + account: &AccountId32, amount: Balance, ) -> Result<(), Error> { - let params = [asset_id.encode(), account.encode(), amount.encode()].concat(); + let params = [token_id.encode(), account.encode(), amount.encode()].concat(); let result = do_bare_call("burn", addr, params); decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } -pub(super) fn create_asset(owner: AccountId32, asset_id: AssetId, min_balance: Balance) -> AssetId { - assert_ok!(Assets::create( - RuntimeOrigin::signed(owner.clone()), - asset_id.into(), - owner.into(), - min_balance - )); - asset_id -} +// Helper functions for interacting with pallet-assets. +pub(super) mod assets { + use super::*; -pub(super) fn mint_asset( - owner: AccountId32, - asset_id: AssetId, - to: AccountId32, - value: Balance, -) -> AssetId { - assert_ok!(Assets::mint( - RuntimeOrigin::signed(owner.clone()), - asset_id.into(), - to.into(), - value - )); - asset_id -} + type AssetId = TokenId; -pub(super) fn create_asset_and_mint_to( - owner: AccountId32, - asset_id: AssetId, - to: AccountId32, - value: Balance, -) -> AssetId { - create_asset(owner.clone(), asset_id, 1); - mint_asset(owner, asset_id, to, value) -} - -// Create an asset, mints to, and approves spender. -pub(super) fn create_asset_mint_and_approve( - owner: AccountId32, - asset_id: AssetId, - to: AccountId32, - mint: Balance, - spender: AccountId32, - approve: Balance, -) -> AssetId { - create_asset_and_mint_to(owner.clone(), asset_id, to.clone(), mint); - assert_ok!(Assets::approve_transfer( - RuntimeOrigin::signed(to.into()), - asset_id.into(), - spender.into(), - approve, - )); - asset_id -} + pub(crate) fn create(owner: &AccountId32, asset_id: AssetId, min_balance: Balance) -> AssetId { + assert_ok!(Assets::create( + RuntimeOrigin::signed(owner.clone()), + asset_id.into(), + owner.clone().into(), + min_balance + )); + asset_id + } -// Freeze an asset. -pub(super) fn freeze_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} + pub(crate) fn mint( + owner: &AccountId32, + asset_id: AssetId, + to: &AccountId32, + value: Balance, + ) -> AssetId { + assert_ok!(Assets::mint( + RuntimeOrigin::signed(owner.clone()), + asset_id.into(), + to.clone().into(), + value + )); + asset_id + } -// Thaw an asset. -pub(super) fn thaw_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} + pub(crate) fn create_and_mint_to( + owner: &AccountId32, + asset_id: AssetId, + to: &AccountId32, + value: Balance, + ) -> AssetId { + create(owner, asset_id, 1); + mint(owner, asset_id, to, value) + } -// Start destroying an asset. -pub(super) fn start_destroy_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} + // Create an asset, mints to, and approves spender. + pub(crate) fn create_mint_and_approve( + owner: &AccountId32, + asset_id: AssetId, + to: &AccountId32, + mint: Balance, + spender: &AccountId32, + approve: Balance, + ) -> AssetId { + create_and_mint_to(owner, asset_id, to, mint); + assert_ok!(Assets::approve_transfer( + RuntimeOrigin::signed(to.clone().into()), + asset_id.into(), + spender.clone().into(), + approve, + )); + asset_id + } -// Create an asset and set metadata. -pub(super) fn create_asset_and_set_metadata( - owner: AccountId32, - asset_id: AssetId, - name: Vec, - symbol: Vec, - decimals: u8, -) -> AssetId { - assert_ok!(Assets::create( - RuntimeOrigin::signed(owner.clone()), - asset_id.into(), - owner.clone().into(), - 100 - )); - set_metadata_asset(owner, asset_id, name, symbol, decimals); - asset_id -} + // Freeze an asset. + pub(crate) fn freeze(owner: &AccountId32, asset_id: AssetId) { + assert_ok!(Assets::freeze_asset( + RuntimeOrigin::signed(owner.clone().into()), + asset_id.into() + )); + } -// Set metadata of an asset. -pub(super) fn set_metadata_asset( - owner: AccountId32, - asset_id: AssetId, - name: Vec, - symbol: Vec, - decimals: u8, -) { - assert_ok!(Assets::set_metadata( - RuntimeOrigin::signed(owner.into()), - asset_id.into(), - name, - symbol, - decimals - )); -} + // Thaw an asset. + pub(crate) fn thaw(owner: &AccountId32, asset_id: AssetId) { + assert_ok!(Assets::thaw_asset( + RuntimeOrigin::signed(owner.clone().into()), + asset_id.into() + )); + } -pub(super) fn token_name_asset(asset_id: AssetId) -> Vec { - as MetadataInspect>::name( - asset_id, - ) -} + // Start destroying an asset. + pub(crate) fn start_destroy(owner: &AccountId32, asset_id: AssetId) { + assert_ok!(Assets::start_destroy( + RuntimeOrigin::signed(owner.clone().into()), + asset_id.into() + )); + } -pub(super) fn token_symbol_asset(asset_id: AssetId) -> Vec { - as MetadataInspect>::symbol( - asset_id, - ) -} + // Create an asset and set metadata. + pub(crate) fn create_and_set_metadata( + owner: &AccountId32, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, + ) -> AssetId { + assert_ok!(Assets::create( + RuntimeOrigin::signed(owner.clone()), + asset_id.into(), + owner.clone().into(), + 100 + )); + set_metadata(owner, asset_id, name, symbol, decimals); + asset_id + } -pub(super) fn token_decimals_asset(asset_id: AssetId) -> u8 { - as MetadataInspect>::decimals( - asset_id, - ) + // Set metadata of an asset. + pub(crate) fn set_metadata( + owner: &AccountId32, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, + ) { + assert_ok!(Assets::set_metadata( + RuntimeOrigin::signed(owner.clone().into()), + asset_id.into(), + name, + symbol, + decimals + )); + } } pub(super) fn instantiate_and_create_fungible( contract: &str, - asset_id: AssetId, + token_id: TokenId, min_balance: Balance, -) -> Result<(), Error> { +) -> Result { let function = function_selector("new"); - let input = [function, asset_id.encode(), min_balance.encode()].concat(); - let (wasm_binary, _) = - load_wasm_module::(contract).expect("could not read .wasm file"); + let input = [function, token_id.encode(), min_balance.encode()].concat(); + let wasm_binary = std::fs::read(contract).expect("could not read .wasm file"); let result = Contracts::bare_instantiate( ALICE, INIT_VALUE, @@ -332,8 +330,10 @@ pub(super) fn instantiate_and_create_fungible( CollectEvents::Skip, ) .result - .expect("should work") - .result; + .expect("should work"); + let address = result.account_id; + let result = result.result; decoded::>(result.clone()) .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) + .map(|_| address) } diff --git a/pop-api/integration-tests/src/lib.rs b/pop-api/integration-tests/src/lib.rs index 6499df59..9e6e20fc 100644 --- a/pop-api/integration-tests/src/lib.rs +++ b/pop-api/integration-tests/src/lib.rs @@ -3,39 +3,36 @@ use frame_support::{ assert_ok, traits::fungibles::{ - approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, Inspect, + approvals::Inspect as _, metadata::Inspect as _, roles::Inspect as _, Inspect, }, weights::Weight, }; use pallet_contracts::{Code, CollectEvents, Determinism, ExecReturnValue}; +use pop_runtime_devnet::{Assets, Contracts, Runtime, RuntimeOrigin, System, UNIT}; use scale::{Decode, Encode}; -use sp_runtime::{traits::Hash, AccountId32, BuildStorage, DispatchError}; - -use pop_runtime_devnet::{ - config::assets::TrustBackedAssetsInstance, Assets, Contracts, Runtime, RuntimeOrigin, System, - UNIT, -}; +use sp_runtime::{AccountId32, BuildStorage, DispatchError}; mod fungibles; -type AssetId = u32; type Balance = u128; const ALICE: AccountId32 = AccountId32::new([1_u8; 32]); const BOB: AccountId32 = AccountId32::new([2_u8; 32]); const DEBUG_OUTPUT: pallet_contracts::DebugInfo = pallet_contracts::DebugInfo::UnsafeDebug; -// FERDIE has no initial balance. const FERDIE: AccountId32 = AccountId32::new([3_u8; 32]); const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); const INIT_AMOUNT: Balance = 100_000_000 * UNIT; const INIT_VALUE: Balance = 100 * UNIT; fn new_test_ext() -> sp_io::TestExternalities { + let _ = env_logger::try_init(); + let mut t = frame_system::GenesisConfig::::default() .build_storage() .expect("Frame system builds valid default genesis config"); pallet_balances::GenesisConfig:: { + // FERDIE has no initial balance. balances: vec![(ALICE, INIT_AMOUNT), (BOB, INIT_AMOUNT)], } .assimilate_storage(&mut t) @@ -46,15 +43,6 @@ fn new_test_ext() -> sp_io::TestExternalities { ext } -fn load_wasm_module(path: &str) -> std::io::Result<(Vec, ::Output)> -where - T: frame_system::Config, -{ - let wasm_binary = std::fs::read(path)?; - let code_hash = T::Hashing::hash(&wasm_binary); - Ok((wasm_binary, code_hash)) -} - fn function_selector(name: &str) -> Vec { let hash = sp_io::hashing::blake2_256(name.as_bytes()); [hash[0..4].to_vec()].concat() @@ -82,8 +70,8 @@ fn bare_call( // Deploy, instantiate and return contract address. fn instantiate(contract: &str, init_value: u128, salt: Vec) -> AccountId32 { - let (wasm_binary, _) = - load_wasm_module::(contract).expect("could not read .wasm file"); + let wasm_binary = std::fs::read(contract).expect("could not read .wasm file"); + let result = Contracts::bare_instantiate( ALICE, init_value, @@ -99,4 +87,4 @@ fn instantiate(contract: &str, init_value: u128, salt: Vec) -> AccountId32 { .unwrap(); assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); result.account_id -} \ No newline at end of file +} diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index c3ac139e..546106e5 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -17,49 +17,16 @@ pub mod primitives; /// The first version of the API. pub mod v0; -/// A result type used by the API, with the `StatusCode` as the error type. +type ChainExtensionMethodApi = ChainExtensionMethod<(), (), (), false>; +/// The result type used by the API, with the `StatusCode` as the error type. pub type Result = core::result::Result; -mod constants { - // Errors: - pub(crate) const DECODING_FAILED: u32 = 255; - // TODO: will be used in the future when the remaining fungibles features will be implemented. - pub(crate) const _MODULE_ERROR: u8 = 3; - - // Function IDs: - pub(crate) const DISPATCH: u8 = 0; - pub(crate) const READ_STATE: u8 = 1; - - // Modules: - pub(crate) const ASSETS: u8 = 52; - pub(crate) const BALANCES: u8 = 10; - pub(crate) const FUNGIBLES: u8 = 150; -} - -// Helper method to build a dispatch call or a call to read state. -// -// Parameters: -// - 'function': The ID of the function. -// - 'version': The version of the chain extension. -// - 'module': The index of the runtime module. -// - 'dispatchable': The index of the module dispatchable functions. -fn build_extension_method( - function: u8, - version: u8, - module: u8, - dispatchable: u8, -) -> ChainExtensionMethod<(), (), (), false> { - ChainExtensionMethod::build(u32::from_le_bytes([function, version, module, dispatchable])) -} - /// Represents a status code returned by the runtime. /// -/// `StatusCode` encapsulates a `u32` value that indicates the status of an operation performed by -/// the runtime. It helps to communicate the success or failure of a Pop API call to the contract, -/// providing a standardized way to handle errors. +/// `StatusCode` encapsulates a `u32` value that indicates the success or failure of a runtime call +/// via Pop API. /// -/// This status code can be used to determine if an operation succeeded or if it encountered an -/// error. A `StatusCode` of `0` typically indicates success, while any other value represents an +/// A `StatusCode` of `0` indicates success, while any other value represents an /// error. #[derive(Debug, Copy, Clone, PartialEq, Eq)] #[ink::scale_derive(Encode, Decode, TypeInfo)] @@ -91,3 +58,33 @@ impl From for StatusCode { StatusCode(DECODING_FAILED) } } + +mod constants { + // Error. + pub(crate) const DECODING_FAILED: u32 = 255; + + // Function IDs. + pub(crate) const DISPATCH: u8 = 0; + pub(crate) const READ_STATE: u8 = 1; + + // Modules. + pub(crate) const ASSETS: u8 = 52; + pub(crate) const BALANCES: u8 = 10; + pub(crate) const FUNGIBLES: u8 = 150; +} + +// Helper method to build a dispatch call or a call to read state. +// +// Parameters: +// - 'function': The ID of the function. +// - 'version': The version of the chain extension. +// - 'module': The index of the runtime module. +// - 'dispatchable': The index of the module dispatchable functions. +fn build_extension_method( + function: u8, + version: u8, + module: u8, + dispatchable: u8, +) -> ChainExtensionMethodApi { + ChainExtensionMethod::build(u32::from_le_bytes([function, version, module, dispatchable])) +} diff --git a/pop-api/src/v0/fungibles.rs b/pop-api/src/v0/fungibles.rs index 5c9b9a30..ecd05b76 100644 --- a/pop-api/src/v0/fungibles.rs +++ b/pop-api/src/v0/fungibles.rs @@ -7,21 +7,21 @@ //! 4. PSP-22 Mintable & Burnable use constants::*; -use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec}; +use ink::prelude::vec::Vec; pub use management::*; pub use metadata::*; use crate::{ constants::{ASSETS, BALANCES, FUNGIBLES}, primitives::{AccountId, Balance, TokenId}, - Result, StatusCode, + ChainExtensionMethodApi, Result, StatusCode, }; // Helper method to build a dispatch call. // // Parameters: // - 'dispatchable': The index of the dispatchable function within the module. -fn build_dispatch(dispatchable: u8) -> ChainExtensionMethod<(), (), (), false> { +fn build_dispatch(dispatchable: u8) -> ChainExtensionMethodApi { crate::v0::build_dispatch(FUNGIBLES, dispatchable) } @@ -29,7 +29,7 @@ fn build_dispatch(dispatchable: u8) -> ChainExtensionMethod<(), (), (), false> { // // Parameters: // - 'state_query': The index of the runtime state query. -fn build_read_state(state_query: u8) -> ChainExtensionMethod<(), (), (), false> { +fn build_read_state(state_query: u8) -> ChainExtensionMethodApi { crate::v0::build_read_state(FUNGIBLES, state_query) } @@ -99,7 +99,7 @@ pub mod events { pub value: u128, } - /// Event emitted when an token is created. + /// Event emitted when a token is created. #[ink::event] pub struct Create { /// The token identifier. @@ -296,7 +296,7 @@ pub fn burn(token: TokenId, account: AccountId, value: Balance) -> Result<()> { .call(&(token, account, value)) } -/// The PSP-22 Metadata interface for querying metadata. +/// The PSP-22 compliant interface for querying metadata. pub mod metadata { use super::*; @@ -526,7 +526,7 @@ mod tests { } #[test] - fn conversion_status_code_into_fungibles_error_works() { + fn converting_status_code_into_fungibles_error_works() { let other_errors = vec![ Other, CannotLookup, diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index 304c3f73..fdc0fdba 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -4,7 +4,7 @@ use crate::{ build_extension_method, constants::{DISPATCH, READ_STATE}, primitives::Error, - StatusCode, + ChainExtensionMethodApi, StatusCode, }; /// APIs for fungible tokens. @@ -24,7 +24,7 @@ impl From for Error { // Parameters: // - 'module': The index of the runtime module. // - 'dispatchable': The index of the module dispatchable functions. -fn build_dispatch(module: u8, dispatchable: u8) -> ChainExtensionMethod<(), (), (), false> { +fn build_dispatch(module: u8, dispatchable: u8) -> ChainExtensionMethodApi { build_extension_method(DISPATCH, V0, module, dispatchable) } @@ -33,6 +33,6 @@ fn build_dispatch(module: u8, dispatchable: u8) -> ChainExtensionMethod<(), (), // Parameters: // - 'module': The index of the runtime module. // - 'state_query': The index of the runtime state query. -fn build_read_state(module: u8, state_query: u8) -> ChainExtensionMethod<(), (), (), false> { +fn build_read_state(module: u8, state_query: u8) -> ChainExtensionMethodApi { build_extension_method(READ_STATE, V0, module, state_query) } diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 0cb46198..e83ea72c 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -9,6 +9,9 @@ version = "0.0.0" codec.workspace = true scale-info.workspace = true +[dev-dependencies] +enum-iterator = "2.1.0" + [features] default = [ "std" ] std = [ "codec/std", "scale-info/std" ] diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 9790b946..8298fc5d 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -3,6 +3,8 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] use codec::{Decode, Encode}; +#[cfg(test)] +use enum_iterator::Sequence; #[cfg(feature = "std")] use scale_info::TypeInfo; pub use v0::*; @@ -20,8 +22,9 @@ pub mod v0 { use super::*; /// Reason why a call failed. - #[derive(Encode, Decode, Debug, Eq, PartialEq, Clone)] - #[cfg_attr(feature = "std", derive(TypeInfo))] + #[derive(Encode, Decode, Debug)] + #[cfg_attr(feature = "std", derive(TypeInfo, Eq, PartialEq, Clone))] + #[cfg_attr(test, derive(Sequence))] #[repr(u8)] #[allow(clippy::unnecessary_cast)] pub enum Error { @@ -105,8 +108,9 @@ pub mod v0 { } /// Description of what went wrong when trying to complete an operation on a token. - #[derive(Encode, Decode, Debug, Eq, PartialEq, Clone)] - #[cfg_attr(feature = "std", derive(TypeInfo))] + #[derive(Encode, Decode, Debug)] + #[cfg_attr(test, derive(Sequence))] + #[cfg_attr(feature = "std", derive(TypeInfo, Eq, PartialEq, Clone))] pub enum TokenError { /// Funds are unavailable. FundsUnavailable, @@ -132,8 +136,9 @@ pub mod v0 { } /// Arithmetic errors. - #[derive(Encode, Decode, Debug, Eq, PartialEq, Clone)] - #[cfg_attr(feature = "std", derive(TypeInfo))] + #[derive(Encode, Decode, Debug)] + #[cfg_attr(test, derive(Sequence))] + #[cfg_attr(feature = "std", derive(TypeInfo, Eq, PartialEq, Clone))] pub enum ArithmeticError { /// Underflow. Underflow, @@ -144,8 +149,9 @@ pub mod v0 { } /// Errors related to transactional storage layers. - #[derive(Encode, Decode, Debug, Eq, PartialEq, Clone)] - #[cfg_attr(feature = "std", derive(TypeInfo))] + #[derive(Encode, Decode, Debug)] + #[cfg_attr(test, derive(Sequence))] + #[cfg_attr(feature = "std", derive(TypeInfo, Eq, PartialEq, Clone))] pub enum TransactionalError { /// Too many transactional layers have been spawned. LimitReached, @@ -156,45 +162,38 @@ pub mod v0 { #[cfg(test)] mod tests { - use super::*; + use enum_iterator::all; + + use super::{Error::*, *}; + + // Conversion method for `Error` to `u32`. + fn convert_error_into_u32(error: &Error) -> u32 { + let mut encoded_error = error.encode(); + encoded_error.resize(4, 0); + u32::from_le_bytes( + encoded_error.try_into().expect("qed, resized to 4 bytes line above"), + ) + } #[test] fn test_error_u32_conversion_with_all_variants() { - let error_variants = vec![ - Error::Other, - Error::CannotLookup, - Error::BadOrigin, - Error::ConsumerRemaining, - Error::NoProviders, - Error::TooManyConsumers, - Error::Token(TokenError::FundsUnavailable), - Error::Arithmetic(ArithmeticError::Underflow), - Error::Transactional(TransactionalError::LimitReached), - Error::Exhausted, - Error::Corruption, - Error::Unavailable, - Error::RootNotAllowed, - Error::DecodingFailed, - Error::Unknown { dispatch_error_index: 1, error_index: 2, error: 3 }, - ]; - // Test conversion for all Error variants - for error in error_variants { - let u32_value: u32 = error.clone().into(); - let decoded_error: Error = u32_value.into(); - assert_eq!(error, decoded_error); - } + all::().collect::>().into_iter().for_each(|error| { + let status_code = u32::from(error.clone()); + let expected = convert_error_into_u32(&error); + assert_eq!(status_code, expected); + let decoded_error = Error::from(status_code); + assert_eq!(decoded_error, error); + }); } #[test] fn test_invalid_u32_values_result_in_decoding_failed() { - // These are u32 values that don't map to any valid Error. - let invalid_u32_values = vec![111u32, 999u32, 1234u32]; - - for invalid_value in invalid_u32_values { + // U32 values that don't map to a valid Error. + vec![111u32, 999u32, 1234u32].into_iter().for_each(|invalid_value| { let error: Error = invalid_value.into(); - assert_eq!(error, Error::DecodingFailed,); - } + assert_eq!(error, DecodingFailed,); + }); } } } diff --git a/runtime/devnet/src/config/api/mod.rs b/runtime/devnet/src/config/api/mod.rs index 569446a3..035a6014 100644 --- a/runtime/devnet/src/config/api/mod.rs +++ b/runtime/devnet/src/config/api/mod.rs @@ -25,7 +25,8 @@ type DecodesAs = pallet_api::extension::DecodesAs< >; /// A query of runtime state. -#[derive(Decode, Debug, PartialEq, Clone)] +#[derive(Decode, Debug)] +#[cfg_attr(test, derive(PartialEq, Clone))] #[repr(u8)] pub enum RuntimeRead { /// Fungible token queries. @@ -54,7 +55,8 @@ impl Readable for RuntimeRead { } /// The result of a runtime state read. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq, Clone))] pub enum RuntimeResult { /// Fungible token read results. Fungibles(fungibles::ReadResult), @@ -165,6 +167,7 @@ impl Contains for Filter { #[cfg(test)] mod tests { + use codec::Encode; use pallet_api::fungibles::Call::*; use sp_core::crypto::AccountId32; use RuntimeCall::{Balances, Fungibles}; @@ -175,8 +178,9 @@ mod tests { #[test] fn runtime_result_encode_works() { - let result = fungibles::ReadResult::::TotalSupply(1_000); - assert_eq!(RuntimeResult::Fungibles(result.clone()).encode(), result.encode()); + let value = 1_000; + let result = fungibles::ReadResult::::TotalSupply(value); + assert_eq!(RuntimeResult::Fungibles(result).encode(), value.encode()); } #[test] @@ -225,7 +229,7 @@ mod tests { } #[test] - fn filter_allows_fungible_reads() { + fn filter_allows_fungibles_reads() { use super::{fungibles::Read::*, RuntimeRead::*}; const READS: [RuntimeRead; 7] = [ Fungibles(TotalSupply(1)), diff --git a/runtime/devnet/src/config/api/versioning.rs b/runtime/devnet/src/config/api/versioning.rs index 0e4cafcb..daa2a0a9 100644 --- a/runtime/devnet/src/config/api/versioning.rs +++ b/runtime/devnet/src/config/api/versioning.rs @@ -39,7 +39,8 @@ impl From for RuntimeRead { } /// Versioned runtime state read results. -#[derive(Debug, PartialEq, Clone)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq, Clone))] pub enum VersionedRuntimeResult { /// Version zero of runtime read results. V0(RuntimeResult), @@ -68,7 +69,8 @@ impl From for Vec { } /// Versioned errors. -#[derive(Debug, PartialEq)] +#[derive(Debug)] +#[cfg_attr(test, derive(PartialEq))] pub enum VersionedError { /// Version zero of errors. V0(pop_primitives::v0::Error), @@ -95,7 +97,8 @@ impl From for u32 { } } -// Type for `pop_primitives::Error` to avoid taking a dependency of sp_runtime on pop-primitives. +// Type for conversion to a versioned `pop_primitives::Error` to avoid taking a dependency of +// sp-runtime on pop-primitives. struct V0Error(pop_primitives::v0::Error); impl From for V0Error { fn from(error: DispatchError) -> Self { @@ -160,6 +163,7 @@ impl From for V0Error { #[cfg(test)] mod tests { + use codec::Encode; use frame_system::Call; use pop_primitives::{ArithmeticError::*, Error, TokenError::*, TransactionalError::*}; use sp_runtime::ModuleError; @@ -181,42 +185,57 @@ mod tests { } #[test] - fn from_versioned_runtime_result_works() { + fn versioned_runtime_result_works() { let result = RuntimeResult::Fungibles(fungibles::ReadResult::::TotalSupply(1_000)); + let v0 = 0; assert_eq!( - VersionedRuntimeResult::try_from((result.clone(), 0)), + VersionedRuntimeResult::try_from((result.clone(), v0)), Ok(VersionedRuntimeResult::V0(result.clone())) ); - // Unknown version. + } + + #[test] + fn versioned_runtime_result_fails() { + // Unknown version 1. assert_eq!( - VersionedRuntimeResult::try_from((result.clone(), 1)), + VersionedRuntimeResult::try_from(( + RuntimeResult::Fungibles(fungibles::ReadResult::::TotalSupply(1_000)), + 1 + )), Err(pallet_contracts::Error::::DecodingFailed.into()) ); } #[test] - fn from_versioned_runtime_result_to_bytes_works() { - let result = RuntimeResult::Fungibles(fungibles::ReadResult::::TotalSupply(1_000)); - assert_eq!(>::from(VersionedRuntimeResult::V0(result.clone())), result.encode()); + fn versioned_runtime_result_to_bytes_works() { + let value = 1_000; + let result = RuntimeResult::Fungibles(fungibles::ReadResult::::TotalSupply(value)); + assert_eq!(>::from(VersionedRuntimeResult::V0(result)), value.encode()); } #[test] - fn from_versioned_error_works() { + fn versioned_error_works() { let error = BadOrigin; + let v0 = 0; + assert_eq!( - VersionedError::try_from((error, 0)), + VersionedError::try_from((error, v0)), Ok(VersionedError::V0(V0Error::from(error).0)) ); - // Unknown version. + } + + #[test] + fn versioned_error_fails() { + // Unknown version 1. assert_eq!( - VersionedError::try_from((error, 1)), + VersionedError::try_from((BadOrigin, 1)), Err(pallet_contracts::Error::::DecodingFailed.into()) ); } #[test] - fn from_versioned_error_to_u32_works() { - assert_eq!(u32::from(VersionedError::V0(Error::BadOrigin)), 2); + fn versioned_error_to_u32_works() { + assert_eq!(u32::from(VersionedError::V0(Error::BadOrigin)), u32::from(Error::BadOrigin)); } // Compare all the different `DispatchError` variants with the expected `Error`. diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 91322ecf..326b7e59 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -96,7 +96,7 @@ impl pallet_nft_fractionalization::Config for Runtime { type WeightInfo = pallet_nft_fractionalization::weights::SubstrateWeight; } -pub type TrustBackedAssetsInstance = pallet_assets::Instance1; +pub(crate) type TrustBackedAssetsInstance = pallet_assets::Instance1; pub type TrustBackedAssetsCall = pallet_assets::Call; impl pallet_assets::Config for Runtime { type ApprovalDeposit = ApprovalDeposit; diff --git a/runtime/devnet/src/config/contracts.rs b/runtime/devnet/src/config/contracts.rs index 7d5113ed..b3701498 100644 --- a/runtime/devnet/src/config/contracts.rs +++ b/runtime/devnet/src/config/contracts.rs @@ -1,23 +1,15 @@ use frame_support::{ parameter_types, - traits::{ConstBool, ConstU32, Contains, Randomness}, + traits::{ConstBool, ConstU32, Nothing, Randomness}, }; use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; use super::api::{self, Config}; use crate::{ - deposit, Balance, Balances, BalancesCall, Perbill, Runtime, RuntimeCall, RuntimeEvent, - RuntimeHoldReason, Timestamp, + deposit, Balance, Balances, Perbill, Runtime, RuntimeCall, RuntimeEvent, RuntimeHoldReason, + Timestamp, }; -pub enum AllowBalancesCall {} - -impl Contains for AllowBalancesCall { - fn contains(call: &RuntimeCall) -> bool { - matches!(call, RuntimeCall::Balances(BalancesCall::transfer_allow_death { .. })) - } -} - fn schedule() -> pallet_contracts::Schedule { pallet_contracts::Schedule { limits: pallet_contracts::Limits { @@ -49,13 +41,8 @@ parameter_types! { impl pallet_contracts::Config for Runtime { type AddressGenerator = pallet_contracts::DefaultAddressGenerator; type ApiVersion = (); - /// The safest default is to allow no calls at all. - /// - /// Runtimes should whitelist dispatchables that are allowed to be called from contracts - /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to - /// change because that would break already deployed contracts. The `RuntimeCall` structure - /// itself is not allowed to change the indices of existing pallets, too. - type CallFilter = AllowBalancesCall; + // IMPORTANT: only runtime calls through the api are allowed. + type CallFilter = Nothing; type CallStack = [pallet_contracts::Frame; 23]; type ChainExtension = api::Extension; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -92,12 +79,12 @@ impl pallet_contracts::Config for Runtime { type Xcm = pallet_xcm::Pallet; } +// IMPORTANT: only runtime calls through the api are allowed. #[test] -fn filter_allows_transfer_allow_death_call() { - assert!(AllowBalancesCall::contains(&RuntimeCall::Balances( - BalancesCall::transfer_allow_death { - dest: sp_runtime::MultiAddress::Address32([0u8; 32]), - value: 0 - } - ))); +fn contracts_prevents_runtime_calls() { + use std::any::TypeId; + assert_eq!( + TypeId::of::<::CallFilter>(), + TypeId::of::() + ); } diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index ba6ceae6..1c799b96 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -995,22 +995,13 @@ cumulus_pallet_parachain_system::register_validate_block! { BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } -#[cfg(test)] -mod tests { +// Ensures that the account id lookup does not perform any state reads. When this changes, +// `pallet_api::fungibles` dispatchables need to be re-evaluated. +#[test] +fn test_lookup_config() { use std::any::TypeId; - - use crate::Runtime; - - // Ensures that the account id lookup does not perform any state reads. When this changes, - // `pallet_api::fungibles` dispatchables need to be re-evaluated. - #[test] - fn test_lookup_config() { - type ExpectedLookup = sp_runtime::traits::AccountIdLookup; - type ConfigLookup = ::Lookup; - - let expected_type_id = TypeId::of::(); - let config_type_id = TypeId::of::(); - - assert_eq!(config_type_id, expected_type_id); - } + assert_eq!( + TypeId::of::<::Lookup>(), + TypeId::of::>() + ); } diff --git a/runtime/testnet/src/config/contracts.rs b/runtime/testnet/src/config/contracts.rs index f052b1d3..5d3c7ee9 100644 --- a/runtime/testnet/src/config/contracts.rs +++ b/runtime/testnet/src/config/contracts.rs @@ -1,22 +1,14 @@ use frame_support::{ parameter_types, - traits::{ConstBool, ConstU32, Randomness}, + traits::{ConstBool, ConstU32, Nothing, Randomness}, }; use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; use crate::{ - deposit, extensions, Balance, Balances, BalancesCall, Perbill, Runtime, RuntimeCall, - RuntimeEvent, RuntimeHoldReason, Timestamp, + deposit, extensions, Balance, Balances, Perbill, Runtime, RuntimeCall, RuntimeEvent, + RuntimeHoldReason, Timestamp, }; -pub enum AllowBalancesCall {} - -impl frame_support::traits::Contains for AllowBalancesCall { - fn contains(call: &RuntimeCall) -> bool { - matches!(call, RuntimeCall::Balances(BalancesCall::transfer_allow_death { .. })) - } -} - fn schedule() -> pallet_contracts::Schedule { pallet_contracts::Schedule { limits: pallet_contracts::Limits { @@ -48,13 +40,8 @@ parameter_types! { impl pallet_contracts::Config for Runtime { type AddressGenerator = pallet_contracts::DefaultAddressGenerator; type ApiVersion = (); - /// The safest default is to allow no calls at all. - /// - /// Runtimes should whitelist dispatchables that are allowed to be called from contracts - /// and make sure they are stable. Dispatchables exposed to contracts are not allowed to - /// change because that would break already deployed contracts. The `RuntimeCall` structure - /// itself is not allowed to change the indices of existing pallets, too. - type CallFilter = AllowBalancesCall; + // IMPORTANT: only runtime calls through the api are allowed. + type CallFilter = Nothing; type CallStack = [pallet_contracts::Frame; 23]; type ChainExtension = extensions::PopApiExtension; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; @@ -90,3 +77,13 @@ impl pallet_contracts::Config for Runtime { type WeightPrice = pallet_transaction_payment::Pallet; type Xcm = pallet_xcm::Pallet; } + +// IMPORTANT: only runtime calls through the api are allowed. +#[test] +fn contracts_prevents_runtime_calls() { + use std::any::TypeId; + assert_eq!( + TypeId::of::<::CallFilter>(), + TypeId::of::() + ); +} diff --git a/runtime/testnet/src/lib.rs b/runtime/testnet/src/lib.rs index c39d9928..e773712e 100644 --- a/runtime/testnet/src/lib.rs +++ b/runtime/testnet/src/lib.rs @@ -1009,3 +1009,14 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } + +// Ensures that the account id lookup does not perform any state reads. When this changes, +// `pallet_api::fungibles` dispatchables need to be re-evaluated. +#[test] +fn test_lookup_config() { + use std::any::TypeId; + assert_eq!( + TypeId::of::<::Lookup>(), + TypeId::of::>() + ); +}