diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index c62f0713..6de57ca4 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -8,11 +8,9 @@ use pop_primitives::error::{ const ASSET_ID: AssetId = 1; const CONTRACT: &str = "contracts/fungibles/target/ink/fungibles.wasm"; -fn decoded(result: ExecReturnValue) -> T { - match ::decode(&mut &result.data[2..]) { - Ok(value) => value, - Err(_) => panic!("\nTest failed by trying to decode `{:?}` into `T`\n", result), - } +fn decoded(result: ExecReturnValue) -> Result { + ::decode(&mut &result.data[2..]) + .map_err(|_| format!("\nTest failed by trying to decode `{:?}` into `T`\n", result)) } // Call total_supply contract message. @@ -20,7 +18,7 @@ fn total_supply(addr: AccountId32, asset_id: AssetId) -> Balance { let function = function_selector("total_supply"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::(result) + decoded::(result).unwrap() } // Call balance_of contract message. @@ -28,7 +26,7 @@ fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Balan let function = function_selector("balance_of"); let params = [function, asset_id.encode(), owner.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::(result) + decoded::(result).unwrap() } // Call allowance contract message. @@ -41,7 +39,7 @@ fn allowance( let function = function_selector("allowance"); let params = [function, asset_id.encode(), owner.encode(), spender.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::(result) + decoded::(result).unwrap() } // Call token_name contract message. @@ -49,7 +47,7 @@ fn token_name(addr: AccountId32, asset_id: AssetId) -> Vec { let function = function_selector("token_name"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result) + decoded::>(result).unwrap() } // Call token_symbol contract message. @@ -57,7 +55,7 @@ fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Vec { let function = function_selector("token_symbol"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result) + decoded::>(result).unwrap() } // Call token_decimals contract message. @@ -65,7 +63,7 @@ fn token_decimals(addr: AccountId32, asset_id: AssetId) -> u8 { let function = function_selector("token_decimals"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::(result) + decoded::(result).unwrap() } fn transfer( @@ -327,7 +325,7 @@ fn transfer_works() { // Asset does not exist. assert_eq!( decoded::(transfer(addr.clone(), 1, BOB, amount,)), - Module { index: 52, error: 3 }, + Ok(Module { index: 52, error: 3 }), ); // Create asset with Alice as owner and mint `amount` to contract address. let asset = create_asset_and_mint_to(ALICE, 1, addr.clone(), amount); @@ -335,18 +333,18 @@ fn transfer_works() { freeze_asset(ALICE, asset); assert_eq!( decoded::(transfer(addr.clone(), asset, BOB, amount,)), - Module { index: 52, error: 16 }, + Ok(Module { index: 52, error: 16 }), ); thaw_asset(ALICE, asset); // Not enough balance. assert_eq!( decoded::(transfer(addr.clone(), asset, BOB, amount + 1 * UNIT)), - Module { index: 52, error: 0 }, + Ok(Module { index: 52, error: 0 }), ); // Not enough balance due to ED. assert_eq!( decoded::(transfer(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 0 }, + Ok(Module { index: 52, error: 0 }), ); // Successful transfer. let balance_before_transfer = Assets::balance(asset, &BOB); @@ -357,13 +355,13 @@ fn transfer_works() { // Transfer asset to account that does not exist. assert_eq!( decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), - Token(CannotCreate) + Ok(Token(CannotCreate)) ); // Asset is not live, i.e. frozen or being destroyed. start_destroy_asset(ALICE, asset); assert_eq!( decoded::(transfer(addr.clone(), asset, BOB, amount / 4)), - Module { index: 52, error: 16 }, + Ok(Module { index: 52, error: 16 }), ); }); } @@ -377,10 +375,13 @@ fn approve_works() { // Asset does not exist. assert_eq!( decoded::(approve(addr.clone(), 0, BOB, amount)), - Module { index: 52, error: 3 }, + Ok(Module { index: 52, error: 3 }), ); let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); - assert_eq!(decoded::(approve(addr.clone(), asset, BOB, amount)), ConsumerRemaining); + assert_eq!( + decoded::(approve(addr.clone(), asset, BOB, amount)), + Ok(ConsumerRemaining) + ); let addr = instantiate(CONTRACT, INIT_VALUE, vec![1]); // Create asset with Alice as owner and mint `amount` to contract address. @@ -389,7 +390,7 @@ fn approve_works() { freeze_asset(ALICE, asset); assert_eq!( decoded::(approve(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 16 }, + Ok(Module { index: 52, error: 16 }), ); thaw_asset(ALICE, asset); // Successful approvals: @@ -403,7 +404,7 @@ fn approve_works() { start_destroy_asset(ALICE, asset); assert_eq!( decoded::(approve(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 16 }, + Ok(Module { index: 52, error: 16 }), ); }); } @@ -417,12 +418,12 @@ fn increase_allowance_works() { // Asset does not exist. assert_eq!( decoded::(increase_allowance(addr.clone(), 0, BOB, amount)), - Module { index: 52, error: 3 }, + Ok(Module { index: 52, error: 3 }), ); let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); assert_eq!( decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - ConsumerRemaining + Ok(ConsumerRemaining) ); let addr = instantiate(CONTRACT, INIT_VALUE, vec![1]); @@ -432,7 +433,7 @@ fn increase_allowance_works() { freeze_asset(ALICE, asset); assert_eq!( decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 16 }, + Ok(Module { index: 52, error: 16 }), ); thaw_asset(ALICE, asset); // Successful approvals: @@ -452,7 +453,7 @@ fn increase_allowance_works() { start_destroy_asset(ALICE, asset); assert_eq!( decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 16 }, + Ok(Module { index: 52, error: 16 }), ); }); } @@ -530,7 +531,7 @@ fn token_metadata_works() { // // Minting can only be done by the owner. // assert_eq!( // decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8])), -// Module { index: 52, error: 2 }, +// Ok(Module { index: 52, error: 2 }), // ); // // Minimum balance of an asset can not be zero. // assert_eq!( @@ -542,7 +543,7 @@ fn token_metadata_works() { // freeze_asset(addr.clone(), asset); // assert_eq!( // decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8])), -// Module { index: 52, error: 16 }, +// Ok(Module { index: 52, error: 16 }), // ); // thaw_asset(addr.clone(), asset); // // Successful mint. @@ -567,7 +568,7 @@ fn token_metadata_works() { // start_destroy_asset(addr.clone(), asset); // assert_eq!( // decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8])), -// Module { index: 52, error: 16 }, +// Ok(Module { index: 52, error: 16 }), // ); // }); // } @@ -582,27 +583,27 @@ fn token_metadata_works() { // // No balance to pay for fees. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), -// Module { index: 10, error: 2 }, +// Ok(Module { index: 10, error: 2 }), // ); // // Instantiate a contract without balance (relay token). // let addr = instantiate(CONTRACT, 100, vec![2]); // // No balance to pay the deposit. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), -// Module { index: 10, error: 2 }, +// Ok(Module { index: 10, error: 2 }), // ); // // Instantiate a contract with balance. // let addr = // instantiate(CONTRACT, INIT_VALUE, vec![1]); // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, BOB, 0)), -// Module { index: 52, error: 7 }, +// Ok(Module { index: 52, error: 7 }), // ); // create_asset(ALICE, ASSET_ID, 1); // // Asset ID is already taken. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, BOB, 1)), -// Module { index: 52, error: 5 }, +// Ok(Module { index: 52, error: 5 }), // ); // // The minimal balance for an asset must be non zero. // let new_asset = 2; diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index a984bb9e..9bf9d50b 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -1,5 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] +use ink::env::chain_extension::{ChainExtensionMethod, FromStatusCode}; + use constants::DECODING_FAILED; use ink::env::chain_extension::FromStatusCode; #[cfg(feature = "assets")] @@ -27,6 +29,22 @@ mod constants { pub(crate) const FUNGIBLES: u8 = 150; } +/// Helper method to build `ChainExtensionMethod`. +/// +/// Parameters: +/// - 'version': The version of the chain extension +/// - 'function': The ID of the function +/// - 'module': The index of the runtime module +/// - 'dispatchable': The index of the module dispatchable functions +fn build_extension_method( + version: u8, + function: u8, + module: u8, + dispatchable: u8, +) -> ChainExtensionMethod<(), (), (), false> { + ChainExtensionMethod::build(u32::from_le_bytes([version, function, module, dispatchable])) +} + /// Represents a status code returned by the runtime. /// /// `StatusCode` encapsulates a `u32` value that indicates the status of an operation performed diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 42373cd7..68ce76b8 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -9,6 +9,22 @@ use crate::{ use constants::*; pub use metadata::*; +/// Helper method to build a dispatch call `ChainExtensionMethod` for fungibles `v0` +/// +/// Parameters: +/// - 'dispatchable': The index of the module dispatchable functions +fn build_dispatch(dispatchable: u8) -> ChainExtensionMethod<(), (), (), false> { + crate::v0::build_dispatch(FUNGIBLES, dispatchable) +} + +/// Helper method to build a dispatch call `ChainExtensionMethod` for fungibles `v0`` +/// +/// Parameters: +/// - 'state_query': The index of the runtime state query +fn build_read_state(state_query: u8) -> ChainExtensionMethod<(), (), (), false> { + crate::v0::build_read_state(FUNGIBLES, state_query) +} + /// Local Fungibles: /// 1. PSP-22 Interface /// 2. PSP-22 Metadata Interface @@ -60,7 +76,7 @@ mod constants { /// The total supply of the token, or an error if the operation fails. #[inline] pub fn total_supply(id: AssetId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOTAL_SUPPLY])) + build_read_state(TOTAL_SUPPLY) .input::() .output::>, true>() .handle_error_code::() @@ -79,7 +95,7 @@ pub fn total_supply(id: AssetId) -> Result { /// The balance of the specified account, or an error if the operation fails. #[inline] pub fn balance_of(id: AssetId, owner: AccountId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, BALANCE_OF])) + build_read_state(BALANCE_OF) .input::<(AssetId, AccountId)>() .output::>, true>() .handle_error_code::() @@ -99,7 +115,7 @@ pub fn balance_of(id: AssetId, owner: AccountId) -> Result { /// The remaining allowance, or an error if the operation fails. #[inline] pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, ALLOWANCE])) + build_read_state(ALLOWANCE) .input::<(AssetId, AccountId, AccountId)>() .output::>, true>() .handle_error_code::() @@ -119,7 +135,7 @@ pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER])) + build_dispatch(TRANSFER) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() @@ -140,7 +156,7 @@ pub fn transfer(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { /// Returns `Ok(())` if successful, or an error if the transfer fails. #[inline] pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER_FROM])) + build_dispatch(TRANSFER_FROM) .input::<(AssetId, AccountId, AccountId, Balance)>() .output::, true>() .handle_error_code::() @@ -158,7 +174,7 @@ pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balanc /// Returns `Ok(())` if successful, or an error if the approval fails. #[inline] pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, APPROVE])) + build_dispatch(APPROVE) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() @@ -176,7 +192,7 @@ pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, INCREASE_ALLOWANCE])) + build_dispatch(INCREASE_ALLOWANCE) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() @@ -194,7 +210,7 @@ pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, DECREASE_ALLOWANCE])) + build_dispatch(DECREASE_ALLOWANCE) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() @@ -212,7 +228,7 @@ pub mod metadata { /// The name of the token as a byte vector, or an error if the operation fails. #[inline] pub fn token_name(id: AssetId) -> Result> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_NAME])) + build_read_state(TOKEN_NAME) .input::() .output::>, true>() .handle_error_code::() @@ -229,7 +245,7 @@ pub mod metadata { /// The symbol of the token as a byte vector, or an error if the operation fails. #[inline] pub fn token_symbol(id: AssetId) -> Result> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_SYMBOL])) + build_read_state(TOKEN_SYMBOL) .input::() .output::>, true>() .handle_error_code::() @@ -246,7 +262,7 @@ pub mod metadata { /// The number of decimals of the token as a byte vector, or an error if the operation fails. #[inline] pub fn token_decimals(id: AssetId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_DECIMALS])) + build_read_state(TOKEN_DECIMALS) .input::() .output::>, true>() .handle_error_code::() diff --git a/pop-api/src/v0/assets/mod.rs b/pop-api/src/v0/assets/mod.rs index 197db710..c68c0181 100644 --- a/pop-api/src/v0/assets/mod.rs +++ b/pop-api/src/v0/assets/mod.rs @@ -1,2 +1,3 @@ #[cfg(feature = "fungibles")] pub mod fungibles; + diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index 1c3642e1..0fd06c89 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -1,4 +1,10 @@ -use crate::{primitives::error::Error, StatusCode}; +use crate::{ + build_extension_method, + constants::{DISPATCH, READ_STATE}, + primitives::error::Error, + StatusCode, +}; +use ink::env::chain_extension::ChainExtensionMethod; #[cfg(feature = "assets")] pub mod assets; @@ -10,3 +16,21 @@ impl From for Error { value.0.into() } } + +/// Helper method to build a dispatch call `ChainExtensionMethod` +/// +/// 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> { + build_extension_method(V0, DISPATCH, module, dispatchable) +} + +/// Helper method to build a dispatch call `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> { + build_extension_method(V0, READ_STATE, module, state_query) +}