From 448cbca490cbd7a52f714a9a6b7730b4ed5e8444 Mon Sep 17 00:00:00 2001 From: Daan van der Plas <93204684+Daanvdplas@users.noreply.github.com> Date: Thu, 12 Sep 2024 18:51:02 +0200 Subject: [PATCH 1/2] refactor: encoding scheme (#271) Co-authored-by: Frank Bell --- Cargo.lock | 21 ++ extension/src/functions.rs | 33 ++- extension/src/matching.rs | 10 +- extension/src/mock.rs | 10 +- pallets/api/src/extension.rs | 215 ++++++++++++++++++-- pop-api/src/lib.rs | 17 +- pop-api/src/v0/mod.rs | 7 +- primitives/Cargo.toml | 3 + primitives/src/lib.rs | 64 +++++- runtime/devnet/src/config/api/versioning.rs | 22 +- 10 files changed, 340 insertions(+), 62 deletions(-) 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 b1a046ea..73a5fdf9 100644 --- a/extension/src/functions.rs +++ b/extension/src/functions.rs @@ -88,7 +88,7 @@ impl< Read: Readable + Debug, Decoder: Decode>, Filter: Contains, - ResultConverter: Converter>>, + ResultConverter: Converter>, Error = DispatchError>, Error: ErrorConverter, Logger: LogTarget, > Function for ReadState @@ -116,7 +116,7 @@ impl< log::debug!(target: Logger::LOG_TARGET, "read: result={result:?}"); // Perform any final conversion. Any implementation is expected to charge weight as // appropriate. - let result = ResultConverter::convert(result, env).into(); + let result = ResultConverter::try_convert(result, env)?.into(); log::debug!(target: Logger::LOG_TARGET, "converted: result={result:?}"); // Charge appropriate weight for writing to contract, based on result length. let weight = ContractWeightsOf::::seal_input(result.len() as u32); @@ -146,12 +146,16 @@ pub trait Readable { fn read(self) -> Self::Result; } -/// Trait for converting a value based on additional information available from the environment. +/// Trait for fallible conversion of a value based on additional information available from the +/// environment. pub trait Converter { + /// The error type returned when conversion fails. + type Error; /// The type of value to be converted. type Source; /// The target type. type Target; + /// The log target. const LOG_TARGET: &'static str; @@ -160,19 +164,29 @@ pub trait Converter { /// # Parameters /// - `value` - The value to be converted. /// - `env` - The current execution environment. - fn convert(value: Self::Source, env: &impl Environment) -> Self::Target; + fn try_convert( + value: Self::Source, + env: &impl Environment, + ) -> core::result::Result; } /// A default converter, for converting (encoding) from some type into a byte array. pub struct DefaultConverter(PhantomData); impl>> Converter for DefaultConverter { + /// The error type returned when conversion fails. + type Error = DispatchError; + /// The type of value to be converted. type Source = T; + /// The target type. type Target = Vec; const LOG_TARGET: &'static str = ""; - fn convert(value: Self::Source, _env: &impl Environment) -> Self::Target { - value.into() + fn try_convert( + value: Self::Source, + _env: &impl Environment, + ) -> core::result::Result { + Ok(value.into()) } } @@ -462,7 +476,7 @@ mod tests { Ok(Converging(0)) )); // Check if the contract environment buffer is written correctly. - assert_eq!(env.buffer, UppercaseConverter::convert(expected, &env)); + assert_eq!(Ok(&env.buffer), UppercaseConverter::try_convert(expected, &env).as_ref()); } #[test] @@ -538,6 +552,9 @@ mod tests { fn default_conversion_works() { let env = MockEnvironment::default(); let source = "pop".to_string(); - assert_eq!(DefaultConverter::convert(source.clone(), &env), source.as_bytes()); + assert_eq!( + DefaultConverter::try_convert(source.clone(), &env), + Ok(source.as_bytes().into()) + ); } } diff --git a/extension/src/matching.rs b/extension/src/matching.rs index 0dcdd34d..94939597 100644 --- a/extension/src/matching.rs +++ b/extension/src/matching.rs @@ -72,13 +72,19 @@ mod tests { } #[test] - fn func_id_matches() { + fn with_func_id_matches() { let env = MockEnvironment::default(); assert!(WithFuncId::>::matches(&env)); + + let env = MockEnvironment::new(1, vec![]); + assert!(WithFuncId::>::matches(&env)); + + let env = MockEnvironment::new(100, vec![]); + assert!(WithFuncId::>::matches(&env)); } #[test] - fn func_id_does_not_match() { + fn with_func_id_does_not_match() { let env = MockEnvironment::new(1, vec![]); assert!(!WithFuncId::>::matches(&env)); } diff --git a/extension/src/mock.rs b/extension/src/mock.rs index 13ffbf47..c085aebe 100644 --- a/extension/src/mock.rs +++ b/extension/src/mock.rs @@ -9,7 +9,7 @@ use frame_support::{ }; use frame_system::{pallet_prelude::BlockNumberFor, EnsureSigned}; use pallet_contracts::{chain_extension::RetVal, DefaultAddressGenerator, Frame, Schedule}; -use sp_runtime::{BuildStorage, Perbill}; +use sp_runtime::{BuildStorage, DispatchError, Perbill}; use crate::{ decoding::Identity, environment, matching::WithFuncId, AccountIdOf, ContractWeightsOf, @@ -373,14 +373,18 @@ pub(crate) fn new_test_ext() -> sp_io::TestExternalities { /// A converter for converting string results to uppercase. pub(crate) struct UppercaseConverter; impl Converter for UppercaseConverter { + type Error = DispatchError; type Source = RuntimeResult; type Target = Vec; const LOG_TARGET: &'static str = ""; - fn convert(value: Self::Source, _env: &impl crate::Environment) -> Self::Target { + fn try_convert( + value: Self::Source, + _env: &impl crate::Environment, + ) -> Result { match value { - RuntimeResult::Pong(value) => value.to_uppercase().encode(), + RuntimeResult::Pong(value) => Ok(value.to_uppercase().encode()), } } } diff --git a/pallets/api/src/extension.rs b/pallets/api/src/extension.rs index 138cdee6..0fd22f2c 100644 --- a/pallets/api/src/extension.rs +++ b/pallets/api/src/extension.rs @@ -2,7 +2,7 @@ use core::{fmt::Debug, marker::PhantomData}; use frame_support::traits::Get; pub use pop_chain_extension::{ - Config, ContractWeightsOf, DecodingFailed, DispatchCall, ReadState, Readable, + Config, ContractWeightsOf, DecodingFailed, DispatchCall, ErrorConverter, ReadState, Readable, }; use pop_chain_extension::{ Converter, Decodes, Environment, LogTarget, Matches, Processor, Result, RetVal, @@ -70,7 +70,7 @@ impl LogTarget for ReadStateLogTarget { /// Conversion of a `DispatchError` to a versioned error. pub struct VersionedErrorConverter(PhantomData); -impl + Into + Debug> pop_chain_extension::ErrorConverter +impl + Into + Debug> ErrorConverter for VersionedErrorConverter { /// The log target. @@ -85,7 +85,7 @@ impl + Into + Debug> pop_chain_extension:: // Defer to supplied versioned error conversion type let version = version(env); log::debug!(target: Self::LOG_TARGET, "versioned error converter: error={error:?}, version={version}"); - let error: Error = (error, version).into(); + let error: Error = (error, version).try_into()?; log::debug!(target: Self::LOG_TARGET, "versioned error converter: converted error={error:?}"); Ok(RetVal::Converging(error.into())) } @@ -93,9 +93,11 @@ impl + Into + Debug> pop_chain_extension:: /// Conversion of a read result to a versioned read result. pub struct VersionedResultConverter(PhantomData<(S, T)>); -impl + Debug> Converter +impl + Debug> Converter for VersionedResultConverter { + /// The type returned in the event of a conversion error. + type Error = DispatchError; /// The type of value to be converted. type Source = Source; /// The target type. @@ -109,47 +111,222 @@ impl + Debug> Converter /// # Parameters /// - `value` - The value to be converted. /// - `env` - The current execution environment. - fn convert(value: Self::Source, env: &impl Environment) -> Self::Target { - // Defer to supplied versioned result conversion type + fn try_convert(value: Self::Source, env: &impl Environment) -> Result { + // 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).into(); + let converted: Target = (value, version).try_into()?; log::debug!(target: Self::LOG_TARGET, "versioned result converter: converted result={converted:?}"); - converted + Ok(converted) } } fn func_id(env: &impl Environment) -> u8 { - // TODO: update once the encoding scheme order has been finalised: expected to be - // env.ext_id().to_le_bytes()[0] - env.func_id().to_le_bytes()[1] + env.func_id().to_le_bytes()[0] } fn module_and_index(env: &impl Environment) -> (u8, u8) { - // TODO: update once the encoding scheme order has been finalised: expected to be - // env.func_id().to_le_bytes()[0..1] let bytes = env.ext_id().to_le_bytes(); (bytes[0], bytes[1]) } fn version(env: &impl Environment) -> u8 { - // TODO: update once the encoding scheme order has been finalised: expected to be - // env.ext_id().to_le_bytes()[1] - env.func_id().to_le_bytes()[0] + env.func_id().to_le_bytes()[1] } #[cfg(test)] mod tests { use frame_support::pallet_prelude::Weight; use pop_chain_extension::Ext; + use sp_core::ConstU8; - use super::*; + use super::{DispatchError::*, *}; use crate::extension::Prepender; + #[test] + fn func_id_works() { + let env = MockEnvironment { func_id: u16::from_le_bytes([1, 2]), ext_id: 0u16 }; + assert_eq!(func_id(&env), 1); + } + + #[test] + fn module_and_index_works() { + let env = MockEnvironment { func_id: 0u16, ext_id: u16::from_le_bytes([2, 3]) }; + assert_eq!(module_and_index(&env), (2, 3)); + } + + #[test] + fn version_works() { + let env = MockEnvironment { func_id: u16::from_le_bytes([1, 2]), ext_id: 0u16 }; + assert_eq!(version(&env), 2); + } + #[test] fn prepender_works() { - let env = MockEnvironment { func_id: 1, ext_id: u16::from_le_bytes([2, 3]) }; - assert_eq!(Prepender::process(vec![0u8], &env), vec![1, 2, 3, 0]); + let func = 0; + let version = 1; + let module = 2; + let index = 3; + let env = MockEnvironment { + func_id: u16::from_le_bytes([func, version]), + ext_id: u16::from_le_bytes([module, index]), + }; + let data = 42; + assert_eq!(Prepender::process(vec![data], &env), vec![version, module, index, data]); + } + + #[test] + fn identified_by_first_byte_of_function_id_matches() { + let env = MockEnvironment { func_id: u16::from_le_bytes([1, 2]), ext_id: 0u16 }; + assert!(IdentifiedByFirstByteOfFunctionId::>::matches(&env)); + } + + #[test] + fn identified_by_first_byte_of_function_id_does_not_match() { + let env = MockEnvironment { func_id: u16::from_le_bytes([1, 2]), ext_id: 0u16 }; + assert!(!IdentifiedByFirstByteOfFunctionId::>::matches(&env)); + } + + #[test] + fn dispatch_call_log_target_works() { + assert!(matches!( + ::LOG_TARGET, + "pop-api::extension::dispatch" + )); + } + + #[test] + fn read_state_log_target_works() { + assert!(matches!( + ::LOG_TARGET, + "pop-api::extension::read-state" + )); + } + + mod versioned_error { + use super::{RetVal::Converging, *}; + + // Mock versioned error. + #[derive(Debug)] + pub enum VersionedError { + V0(DispatchError), + V1(DispatchError), + } + + impl TryFrom<(DispatchError, u8)> for VersionedError { + type Error = DispatchError; + + fn try_from(value: (DispatchError, u8)) -> Result { + let (error, version) = value; + match version { + 0 => Ok(VersionedError::V0(error)), + 1 => Ok(VersionedError::V1(error)), + _ => Err(Other("DecodingFailed")), + } + } + } + + impl From for u32 { + // Mock conversion based on error and version. + fn from(value: VersionedError) -> Self { + match value { + VersionedError::V0(error) => match error { + BadOrigin => 1, + _ => 100, + }, + VersionedError::V1(error) => match error { + BadOrigin => 2, + _ => 200, + }, + } + } + } + + #[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); + } + } + + #[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"))); + } + } + + mod versioned_result { + use VersionedRuntimeResult::{V0, V1}; + + use super::*; + + // Mock versioned runtime result. + #[derive(Debug, PartialEq)] + pub enum VersionedRuntimeResult { + V0(u8), + V1(u8), + } + + impl TryFrom<(u8, u8)> for VersionedRuntimeResult { + type Error = DispatchError; + + // 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)), + 1 if result <= 100 => Ok(V1(result)), + 1 if result > 100 => Ok(V1(100)), + _ => Err(Other("DecodingFailed")), + } + } + } + + #[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(10, &env).err(); + assert_eq!(result, Some(Other("DecodingFailed"))); + } } struct MockEnvironment { diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index 0a080c56..c3ac139e 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -1,9 +1,10 @@ -//! The `pop-api` crate provides an API for smart contracts to interact with the Pop Network runtime. +//! The `pop-api` crate provides an API for smart contracts to interact with the Pop Network +//! runtime. //! -//! This crate abstracts away complexities to deliver a streamlined developer experience while supporting -//! multiple API versions to ensure backward compatibility. It is designed with a focus on stability, -//! future-proofing, and storage efficiency, allowing developers to easily integrate powerful runtime -//! features into their contracts without unnecessary overhead. +//! This crate abstracts away complexities to deliver a streamlined developer experience while +//! supporting multiple API versions to ensure backward compatibility. It is designed with a focus +//! on stability, future-proofing, and storage efficiency, allowing developers to easily integrate +//! powerful runtime features into their contracts without unnecessary overhead. #![cfg_attr(not(feature = "std"), no_std, no_main)] @@ -38,17 +39,17 @@ mod constants { // Helper method to build a dispatch call or a call to read state. // // Parameters: -// - 'version': The version of the chain extension. // - '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( - version: u8, function: u8, + version: u8, module: u8, dispatchable: u8, ) -> ChainExtensionMethod<(), (), (), false> { - ChainExtensionMethod::build(u32::from_le_bytes([version, function, module, dispatchable])) + ChainExtensionMethod::build(u32::from_le_bytes([function, version, module, dispatchable])) } /// Represents a status code returned by the runtime. diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index 8ed35058..304c3f73 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -1,10 +1,11 @@ +use ink::env::chain_extension::ChainExtensionMethod; + use crate::{ build_extension_method, constants::{DISPATCH, READ_STATE}, primitives::Error, StatusCode, }; -use ink::env::chain_extension::ChainExtensionMethod; /// APIs for fungible tokens. #[cfg(feature = "fungibles")] @@ -24,7 +25,7 @@ impl From for Error { // - 'module': The index of the runtime module. // - 'dispatchable': The index of the module dispatchable functions. fn build_dispatch(module: u8, dispatchable: u8) -> ChainExtensionMethod<(), (), (), false> { - build_extension_method(V0, DISPATCH, module, dispatchable) + build_extension_method(DISPATCH, V0, module, dispatchable) } // Helper method to build a call to read state. @@ -33,5 +34,5 @@ fn build_dispatch(module: u8, dispatchable: u8) -> ChainExtensionMethod<(), (), // - '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> { - build_extension_method(V0, READ_STATE, module, state_query) + 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 e6942432..8298fc5d 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,6 +1,10 @@ +//! The `pop-primitives` crate provides types used by other crates. + #![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::*; @@ -8,6 +12,7 @@ pub use v0::*; /// The identifier of a token. pub type TokenId = u32; +/// The first version of primitives' types. pub mod v0 { pub use error::*; @@ -16,9 +21,10 @@ pub mod v0 { mod error { use super::*; - /// Reason why a Pop API call failed. - #[derive(Encode, Decode, Debug, Eq, PartialEq)] - #[cfg_attr(feature = "std", derive(TypeInfo))] + /// Reason why a call failed. + #[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 { @@ -102,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)] - #[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, @@ -129,8 +136,9 @@ pub mod v0 { } /// Arithmetic errors. - #[derive(Encode, Decode, Debug, Eq, PartialEq)] - #[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, @@ -141,8 +149,9 @@ pub mod v0 { } /// Errors related to transactional storage layers. - #[derive(Encode, Decode, Debug, Eq, PartialEq)] - #[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, @@ -150,4 +159,41 @@ pub mod v0 { NoLayer, } } + + #[cfg(test)] + mod tests { + 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() { + // Test conversion for all Error variants + 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() { + // 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, DecodingFailed,); + }); + } + } } diff --git a/runtime/devnet/src/config/api/versioning.rs b/runtime/devnet/src/config/api/versioning.rs index 317496df..2f3d240f 100644 --- a/runtime/devnet/src/config/api/versioning.rs +++ b/runtime/devnet/src/config/api/versioning.rs @@ -45,14 +45,15 @@ pub enum VersionedRuntimeResult { V0(RuntimeResult), } -impl From<(RuntimeResult, Version)> for VersionedRuntimeResult { - fn from(value: (RuntimeResult, Version)) -> Self { +impl TryFrom<(RuntimeResult, Version)> for VersionedRuntimeResult { + type Error = DispatchError; + + fn try_from(value: (RuntimeResult, Version)) -> Result { let (result, version) = value; match version { // Allows mapping from current `RuntimeResult` to a specific/prior version - 0 => VersionedRuntimeResult::V0(result), - // TODO: should never occur due to version processing/validation when request received - _ => unimplemented!(), + 0 => Ok(VersionedRuntimeResult::V0(result)), + _ => Err(pallet_contracts::Error::::DecodingFailed.into()), } } } @@ -73,14 +74,15 @@ pub enum VersionedError { V0(pop_primitives::v0::Error), } -impl From<(DispatchError, Version)> for VersionedError { - fn from(value: (DispatchError, Version)) -> Self { +impl TryFrom<(DispatchError, Version)> for VersionedError { + type Error = DispatchError; + + fn try_from(value: (DispatchError, Version)) -> Result { let (error, version) = value; match version { // Allows mapping from current `DispatchError` to a specific/prior version of `Error` - 0 => VersionedError::V0(V0Error::from(error).0), - // TODO: should never occur due to version processing/validation when request received - _ => unimplemented!(), + 0 => Ok(VersionedError::V0(V0Error::from(error).0)), + _ => Err(pallet_contracts::Error::::DecodingFailed.into()), } } } From 7bbc04c845185e738492c2818d2fa182e5235557 Mon Sep 17 00:00:00 2001 From: Daan van der Plas <93204684+Daanvdplas@users.noreply.github.com> Date: Fri, 13 Sep 2024 16:50:29 +0200 Subject: [PATCH 2/2] refactor: api integration tests (#280) --- pop-api/examples/fungibles/lib.rs | 137 ++--- .../create_token_in_constructor/Cargo.toml | 1 - .../contracts/fungibles/Cargo.toml | 1 - .../integration-tests/src/fungibles/mod.rs | 476 ++++++++---------- .../integration-tests/src/fungibles/utils.rs | 348 ++++++------- pop-api/integration-tests/src/lib.rs | 30 +- pop-api/src/lib.rs | 73 ++- pop-api/src/v0/fungibles.rs | 14 +- pop-api/src/v0/mod.rs | 6 +- runtime/devnet/src/config/assets.rs | 2 +- 10 files changed, 465 insertions(+), 623 deletions(-) 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/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;