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/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/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;