From 360bb2a93b79085abb30a979b41fe5d77e46b37f Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Fri, 19 Jul 2024 18:17:19 +0200 Subject: [PATCH 01/56] feat: pop api fungibles pallet --- Cargo.lock | 16 ++++++ Cargo.toml | 1 + pallets/fungibles/Cargo.toml | 50 +++++++++++++++++ pallets/fungibles/src/benchmarking.rs | 22 ++++++++ pallets/fungibles/src/lib.rs | 64 ++++++++++++++++++++++ pallets/fungibles/src/mock.rs | 60 ++++++++++++++++++++ pallets/fungibles/src/tests.rs | 22 ++++++++ runtime/devnet/Cargo.toml | 2 + runtime/devnet/src/config/assets.rs | 2 + runtime/devnet/src/extensions/mod.rs | 5 +- runtime/devnet/src/extensions/v0/assets.rs | 30 ++++++---- runtime/devnet/src/lib.rs | 5 +- 12 files changed, 263 insertions(+), 16 deletions(-) create mode 100644 pallets/fungibles/Cargo.toml create mode 100644 pallets/fungibles/src/benchmarking.rs create mode 100644 pallets/fungibles/src/lib.rs create mode 100644 pallets/fungibles/src/mock.rs create mode 100644 pallets/fungibles/src/tests.rs diff --git a/Cargo.lock b/Cargo.lock index 1983b157..469b0449 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7219,6 +7219,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-fungibles" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-assets", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-grandpa" version = "29.0.0" @@ -9745,6 +9760,7 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-contracts", + "pallet-fungibles", "pallet-message-queue", "pallet-multisig", "pallet-nft-fractionalization", diff --git a/Cargo.toml b/Cargo.toml index 6b3cde02..31b5acda 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,7 @@ members = [ "runtime/devnet", "runtime/testnet", "integration-tests", + "pallets/*", "primitives", "scripts/fund-dev-accounts", ] diff --git a/pallets/fungibles/Cargo.toml b/pallets/fungibles/Cargo.toml new file mode 100644 index 00000000..7c3f44e4 --- /dev/null +++ b/pallets/fungibles/Cargo.toml @@ -0,0 +1,50 @@ +[package] +name = "pallet-fungibles" +authors = ["Anonymous"] +description = "Frame Pallet" +version = "0.1.0" +license = "Unlicense" +edition = "2021" + +[package.metadata.docs.rs] +targets = ["x86_64-unknown-linux-gnu"] + +[dependencies] +codec.workspace = true +scale-info.workspace = true + +# Substrate +frame-benchmarking.workspace = true +frame-support.workspace = true +frame-system.workspace = true +pallet-assets.workspace = true +sp-runtime.workspace = true + +[dev-dependencies] +sp-core.workspace = true +sp-io.workspace = true + +[features] +default = ["std"] +runtime-benchmarks = [ + "frame-benchmarking/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "sp-runtime/runtime-benchmarks", +] +std = [ + "codec/std", + "frame-benchmarking/std", + "frame-support/std", + "frame-system/std", + "pallet-assets/std", + "scale-info/std", + "sp-core/std", + "sp-io/std", + "sp-runtime/std", +] +try-runtime = [ + "frame-support/try-runtime", + "frame-system/try-runtime", + "sp-runtime/try-runtime", +] diff --git a/pallets/fungibles/src/benchmarking.rs b/pallets/fungibles/src/benchmarking.rs new file mode 100644 index 00000000..f3b0b0ec --- /dev/null +++ b/pallets/fungibles/src/benchmarking.rs @@ -0,0 +1,22 @@ +//! Benchmarking setup for pallet-parachain-template + +// https://docs.substrate.io/test/benchmark/ + +use super::*; + +#[allow(unused)] +use crate::Pallet as Template; +use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; +use frame_system::RawOrigin; + +benchmarks! { + do_something { + let s in 0 .. 100; + let caller: T::AccountId = whitelisted_caller(); + }: _(RawOrigin::Signed(caller), s) + verify { + assert_eq!(Something::::get(), Some(s)); + } +} + +impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/pallets/fungibles/src/lib.rs b/pallets/fungibles/src/lib.rs new file mode 100644 index 00000000..5833f02a --- /dev/null +++ b/pallets/fungibles/src/lib.rs @@ -0,0 +1,64 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod mock; + +#[cfg(test)] +mod tests; + +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::{pallet_prelude::*, traits::fungibles::Inspect}; + use frame_system::pallet_prelude::*; + use sp_runtime::traits::StaticLookup; + + type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; + type AssetIdOf = as Inspect< + ::AccountId, + >>::AssetId; + type AssetIdParameterOf = + >::AssetIdParameter; + type BalanceOf = as Inspect< + ::AccountId, + >>::Balance; + // Should be defined in primitives. + type TrustBackedAssetsInstance = pallet_assets::Instance1; + + #[pallet::config] + pub trait Config: + frame_system::Config + pallet_assets::Config + { + } + + #[pallet::pallet] + pub struct Pallet(_); + + use pallet_assets::WeightInfo; + + #[pallet::call] + impl Pallet { + #[pallet::call_index(0)] + #[pallet::weight(>::WeightInfo::transfer_keep_alive())] + pub fn transfer( + origin: OriginFor, + id: AssetIdParameterOf, + target: AccountIdLookupOf, + amount: BalanceOf, + ) -> DispatchResult { + pallet_assets::Pallet::::transfer_keep_alive( + origin, id, target, amount, + ) + } + } + + impl Pallet { + pub fn total_supply(id: AssetIdOf) -> BalanceOf { + pallet_assets::Pallet::::total_supply(id) + } + } +} diff --git a/pallets/fungibles/src/mock.rs b/pallets/fungibles/src/mock.rs new file mode 100644 index 00000000..82412fe3 --- /dev/null +++ b/pallets/fungibles/src/mock.rs @@ -0,0 +1,60 @@ +use frame_support::{parameter_types, traits::Everything}; +use frame_system as system; +use sp_core::H256; +use sp_runtime::{ + traits::{BlakeTwo256, IdentityLookup}, + BuildStorage, +}; + +type Block = frame_system::mocking::MockBlock; + +// Configure a mock runtime to test the pallet. +frame_support::construct_runtime!( + pub enum Test + { + System: frame_system::{Pallet, Call, Config, Storage, Event}, + fungibles: crate::{Pallet, Call, Storage, Event}, + } +); + +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const SS58Prefix: u8 = 42; +} + +#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] +impl system::Config for Test { + type BaseCallFilter = Everything; + type BlockWeights = (); + type BlockLength = (); + type DbWeight = (); + type RuntimeOrigin = RuntimeOrigin; + type RuntimeCall = RuntimeCall; + type Nonce = u64; + type Hash = H256; + type Hashing = BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Block = Block; + type RuntimeEvent = RuntimeEvent; + type BlockHashCount = BlockHashCount; + type Version = (); + type PalletInfo = PalletInfo; + type AccountData = (); + type OnNewAccount = (); + type OnKilledAccount = (); + type SystemWeightInfo = (); + type SS58Prefix = SS58Prefix; + type OnSetCode = (); + type MaxConsumers = frame_support::traits::ConstU32<16>; +} + +impl crate::Config for Test { + type RuntimeEvent = RuntimeEvent; + type WeightInfo = (); +} + +// Build genesis storage according to the mock runtime. +pub fn new_test_ext() -> sp_io::TestExternalities { + system::GenesisConfig::::default().build_storage().unwrap().into() +} diff --git a/pallets/fungibles/src/tests.rs b/pallets/fungibles/src/tests.rs new file mode 100644 index 00000000..a5116a72 --- /dev/null +++ b/pallets/fungibles/src/tests.rs @@ -0,0 +1,22 @@ +use crate::{mock::*, Error}; +use frame_support::{assert_noop, assert_ok}; + +// https://docs.substrate.io/test/ + +#[test] +fn it_works_for_default_value() { + new_test_ext().execute_with(|| { + // Dispatch a signed extrinsic. + assert_ok!(fungibles::do_something(RuntimeOrigin::signed(1), 42)); + // Read pallet storage and assert an expected result. + assert_eq!(fungibles::something(), Some(42)); + }); +} + +#[test] +fn correct_error_for_none_value() { + new_test_ext().execute_with(|| { + // Ensure the expected error is thrown when no value is present. + assert_noop!(fungibles::cause_error(RuntimeOrigin::signed(1)), Error::::NoneValue); + }); +} diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 70e6e9c9..d2c03dbe 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -24,6 +24,7 @@ smallvec.workspace = true # Local pop-primitives = { workspace = true, features = ["assets", "cross-chain", "nfts"] } pop-runtime-common = { workspace = true, default-features = false } +pallet-fungibles = { path = "../../pallets/fungibles/", default-features = false } # Substrate frame-benchmarking.workspace = true @@ -119,6 +120,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-contracts/std", + "pallet-fungibles/std", "pallet-message-queue/std", "pallet-multisig/std", "pallet-nft-fractionalization/std", diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 2c8ea952..dd7cb6c9 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -119,3 +119,5 @@ impl pallet_assets::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); } + +impl pallet_fungibles::Config for Runtime {} diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index d2bd63e7..e6f0e287 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -174,7 +174,7 @@ fn construct_call( match pallet_index { index if index == super::Assets::index() as u8 => { let call = versioned_construct_assets_call(version, call_index, params)?; - Ok(RuntimeCall::Assets(call)) + Ok(RuntimeCall::Fungibles(call)) }, _ => Err(DispatchError::Other("UnknownFunctionId")), } @@ -199,7 +199,8 @@ fn versioned_construct_assets_call( version: u8, call_index: u8, params: Vec, -) -> Result, DispatchError> { + // ) -> Result, DispatchError> { +) -> Result, DispatchError> { match version { V0 => v0::assets::construct_assets_call(call_index, params), _ => Err(DispatchError::Other("UnknownFunctionId")), diff --git a/runtime/devnet/src/extensions/v0/assets.rs b/runtime/devnet/src/extensions/v0/assets.rs index c6b15b4e..3411368f 100644 --- a/runtime/devnet/src/extensions/v0/assets.rs +++ b/runtime/devnet/src/extensions/v0/assets.rs @@ -49,27 +49,33 @@ pub(crate) fn construct_assets_key( pub(crate) fn construct_assets_call( call_index: u8, params: Vec, -) -> Result, DispatchError> { + // ) -> Result, DispatchError> { +) -> Result, DispatchError> { match call_index { 9 => { let (id, target, amount) = <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(pallet_assets::Call::::transfer_keep_alive { + // Ok(pallet_assets::Call::::transfer_keep_alive { + // id: Compact(id), + // target: MultiAddress::Id(target), + // amount, + // }) + Ok(pallet_fungibles::Call::::transfer { id: Compact(id), target: MultiAddress::Id(target), amount, }) }, - 22 => { - let (id, delegate, amount) = - <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(pallet_assets::Call::::approve_transfer { - id: Compact(id), - delegate: MultiAddress::Id(delegate), - amount, - }) - }, + // 22 => { + // let (id, delegate, amount) = + // <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) + // .map_err(|_| DispatchError::Other("DecodingFailed"))?; + // Ok(pallet_assets::Call::::approve_transfer { + // id: Compact(id), + // delegate: MultiAddress::Id(delegate), + // amount, + // }) + // }, // other calls _ => Err(DispatchError::Other("UnknownFunctionId")), } diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 9ab64043..822fc24d 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -252,7 +252,7 @@ impl Contains for FilteredCalls { /// A type to identify allowed calls to the Runtime from contracts. Used by Pop API pub struct AllowedApiCalls; -impl Contains for crate::AllowedApiCalls { +impl Contains for AllowedApiCalls { fn contains(c: &RuntimeCall) -> bool { use config::assets::AssetsCall; use pallet_nfts::Call as NftsCall; @@ -321,7 +321,7 @@ impl Contains for crate::AllowedApiCalls { | NftsCall::create_swap { .. } | NftsCall::cancel_swap { .. } | NftsCall::claim_swap { .. } - ) + ) | RuntimeCall::Fungibles(pallet_fungibles::Call::transfer { .. }) ) } } @@ -663,6 +663,7 @@ construct_runtime!( Nfts: pallet_nfts = 50, NftFractionalization: pallet_nft_fractionalization = 51, Assets: pallet_assets:: = 52, + Fungibles: pallet_fungibles = 53, } ); From e097f8014a27b4031324920726de0ca578c51fe8 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Sat, 20 Jul 2024 16:11:46 +0200 Subject: [PATCH 02/56] refactor: comments --- Cargo.lock | 32 +++++++-------- Cargo.toml | 1 + pallets/{fungibles => api}/Cargo.toml | 10 ++--- .../src/lib.rs => api/src/fungibles.rs} | 36 +++++++++++++---- .../src => api/src/fungibles}/benchmarking.rs | 0 .../src => api/src/fungibles}/tests.rs | 0 pallets/api/src/lib.rs | 3 ++ pallets/{fungibles => api}/src/mock.rs | 0 runtime/devnet/Cargo.toml | 6 +-- runtime/devnet/src/config/assets.rs | 11 ++--- runtime/devnet/src/extensions/mod.rs | 6 +-- runtime/devnet/src/extensions/v0/assets.rs | 40 +++++++++++-------- runtime/devnet/src/lib.rs | 8 +++- 13 files changed, 95 insertions(+), 58 deletions(-) rename pallets/{fungibles => api}/Cargo.toml (83%) rename pallets/{fungibles/src/lib.rs => api/src/fungibles.rs} (56%) rename pallets/{fungibles/src => api/src/fungibles}/benchmarking.rs (100%) rename pallets/{fungibles/src => api/src/fungibles}/tests.rs (100%) create mode 100644 pallets/api/src/lib.rs rename pallets/{fungibles => api}/src/mock.rs (100%) diff --git a/Cargo.lock b/Cargo.lock index 469b0449..31848492 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7219,21 +7219,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-fungibles" -version = "0.1.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-assets", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", -] - [[package]] name = "pallet-grandpa" version = "29.0.0" @@ -7552,6 +7537,21 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-pop-api" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-assets", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-preimage" version = "29.0.0" @@ -9760,12 +9760,12 @@ dependencies = [ "pallet-balances", "pallet-collator-selection", "pallet-contracts", - "pallet-fungibles", "pallet-message-queue", "pallet-multisig", "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", + "pallet-pop-api", "pallet-preimage", "pallet-proxy", "pallet-scheduler", diff --git a/Cargo.toml b/Cargo.toml index 31b5acda..e727f002 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,6 +52,7 @@ substrate-wasm-builder = "18.0.1" substrate-build-script-utils = "11.0.0" # Local +pallet-pop-api = { path = "pallets/api", default-features = false } pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-common = { path = "runtime/common", default-features = false } diff --git a/pallets/fungibles/Cargo.toml b/pallets/api/Cargo.toml similarity index 83% rename from pallets/fungibles/Cargo.toml rename to pallets/api/Cargo.toml index 7c3f44e4..5973b794 100644 --- a/pallets/fungibles/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -1,10 +1,10 @@ [package] -name = "pallet-fungibles" -authors = ["Anonymous"] -description = "Frame Pallet" +name = "pallet-pop-api" +authors.workspace = true +description = "Pop api pallet, enabling smart contracts to use the power of Polkadot" +edition.workspace = true +license.workspace = true version = "0.1.0" -license = "Unlicense" -edition = "2021" [package.metadata.docs.rs] targets = ["x86_64-unknown-linux-gnu"] diff --git a/pallets/fungibles/src/lib.rs b/pallets/api/src/fungibles.rs similarity index 56% rename from pallets/fungibles/src/lib.rs rename to pallets/api/src/fungibles.rs index 5833f02a..26009a5a 100644 --- a/pallets/fungibles/src/lib.rs +++ b/pallets/api/src/fungibles.rs @@ -13,8 +13,11 @@ mod benchmarking; #[frame_support::pallet] pub mod pallet { - use frame_support::{pallet_prelude::*, traits::fungibles::Inspect}; + use frame_support::{ + dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::fungibles::Inspect, + }; use frame_system::pallet_prelude::*; + use pallet_assets::WeightInfo; use sp_runtime::traits::StaticLookup; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; @@ -23,6 +26,8 @@ pub mod pallet { >>::AssetId; type AssetIdParameterOf = >::AssetIdParameter; + type Assets = pallet_assets::Pallet; + type AssetsWeightInfo = >::WeightInfo; type BalanceOf = as Inspect< ::AccountId, >>::Balance; @@ -38,27 +43,42 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); - use pallet_assets::WeightInfo; - #[pallet::call] impl Pallet { #[pallet::call_index(0)] - #[pallet::weight(>::WeightInfo::transfer_keep_alive())] + #[pallet::weight(AssetsWeightInfo::::transfer_keep_alive())] pub fn transfer( origin: OriginFor, id: AssetIdParameterOf, target: AccountIdLookupOf, amount: BalanceOf, ) -> DispatchResult { - pallet_assets::Pallet::::transfer_keep_alive( - origin, id, target, amount, - ) + Assets::::transfer_keep_alive(origin, id, target, amount) + } + + #[pallet::call_index(1)] + #[pallet::weight(AssetsWeightInfo::::cancel_approval())] + pub fn approve( + origin: OriginFor, + id: AssetIdParameterOf, + spender: AccountIdLookupOf, + value: BalanceOf, + ) -> DispatchResultWithPostInfo { + Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; + Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { + e.with_weight( + AssetsWeightInfo::::cancel_approval() + + AssetsWeightInfo::::approve_transfer(), + ) + })?; + Ok(().into()) } } impl Pallet { pub fn total_supply(id: AssetIdOf) -> BalanceOf { - pallet_assets::Pallet::::total_supply(id) + Assets::::total_supply(id) } } } diff --git a/pallets/fungibles/src/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs similarity index 100% rename from pallets/fungibles/src/benchmarking.rs rename to pallets/api/src/fungibles/benchmarking.rs diff --git a/pallets/fungibles/src/tests.rs b/pallets/api/src/fungibles/tests.rs similarity index 100% rename from pallets/fungibles/src/tests.rs rename to pallets/api/src/fungibles/tests.rs diff --git a/pallets/api/src/lib.rs b/pallets/api/src/lib.rs new file mode 100644 index 00000000..3aaad4c2 --- /dev/null +++ b/pallets/api/src/lib.rs @@ -0,0 +1,3 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub mod fungibles; diff --git a/pallets/fungibles/src/mock.rs b/pallets/api/src/mock.rs similarity index 100% rename from pallets/fungibles/src/mock.rs rename to pallets/api/src/mock.rs diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index d2c03dbe..c4a1fe7c 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -23,8 +23,8 @@ smallvec.workspace = true # Local pop-primitives = { workspace = true, features = ["assets", "cross-chain", "nfts"] } -pop-runtime-common = { workspace = true, default-features = false } -pallet-fungibles = { path = "../../pallets/fungibles/", default-features = false } +pop-runtime-common.workspace = true +pallet-pop-api.workspace = true # Substrate frame-benchmarking.workspace = true @@ -120,7 +120,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-contracts/std", - "pallet-fungibles/std", + "pallet-pop-api/std", "pallet-message-queue/std", "pallet-multisig/std", "pallet-nft-fractionalization/std", diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index dd7cb6c9..5c506a41 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -1,7 +1,3 @@ -use crate::{ - deposit, AccountId, Assets, Balance, Balances, BlockNumber, Nfts, Runtime, RuntimeEvent, - RuntimeHoldReason, DAYS, EXISTENTIAL_DEPOSIT, UNIT, -}; use frame_support::{ parameter_types, traits::{AsEnsureOriginWithArg, ConstU32}, @@ -12,6 +8,11 @@ use pallet_nfts::PalletFeatures; use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId, Signature}; use sp_runtime::traits::Verify; +use crate::{ + deposit, fungibles, AccountId, Assets, Balance, Balances, BlockNumber, Nfts, Runtime, + RuntimeEvent, RuntimeHoldReason, DAYS, EXISTENTIAL_DEPOSIT, UNIT, +}; + /// We allow root to execute privileged asset operations. pub type AssetsForceOrigin = EnsureRoot; @@ -120,4 +121,4 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } -impl pallet_fungibles::Config for Runtime {} +impl fungibles::Config for Runtime {} diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index e6f0e287..7bbe7954 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -24,8 +24,8 @@ use xcm::{ }; use crate::{ - config::assets::TrustBackedAssetsInstance, AccountId, AllowedApiCalls, Balance, Runtime, - RuntimeCall, RuntimeOrigin, UNIT, + config::assets::TrustBackedAssetsInstance, fungibles, AccountId, AllowedApiCalls, Balance, + Runtime, RuntimeCall, RuntimeOrigin, UNIT, }; use pop_primitives::{ cross_chain::CrossChainMessage, @@ -200,7 +200,7 @@ fn versioned_construct_assets_call( call_index: u8, params: Vec, // ) -> Result, DispatchError> { -) -> Result, DispatchError> { +) -> Result, DispatchError> { match version { V0 => v0::assets::construct_assets_call(call_index, params), _ => Err(DispatchError::Other("UnknownFunctionId")), diff --git a/runtime/devnet/src/extensions/v0/assets.rs b/runtime/devnet/src/extensions/v0/assets.rs index 3411368f..d385028d 100644 --- a/runtime/devnet/src/extensions/v0/assets.rs +++ b/runtime/devnet/src/extensions/v0/assets.rs @@ -1,7 +1,10 @@ -use crate::extensions::{ - AccountId as AccountId32, AssetId, - AssetsKeys::{self, *}, - Balance, Compact, Decode, DispatchError, MultiAddress, Runtime, TrustBackedAssetsInstance, +use crate::{ + extensions::{ + AccountId as AccountId32, AssetId, + AssetsKeys::{self, *}, + Balance, Compact, Decode, DispatchError, MultiAddress, Runtime, TrustBackedAssetsInstance, + }, + fungibles, }; use pop_primitives::AccountId; use sp_std::vec::Vec; @@ -50,7 +53,7 @@ pub(crate) fn construct_assets_call( call_index: u8, params: Vec, // ) -> Result, DispatchError> { -) -> Result, DispatchError> { +) -> Result, DispatchError> { match call_index { 9 => { let (id, target, amount) = <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) @@ -60,22 +63,27 @@ pub(crate) fn construct_assets_call( // target: MultiAddress::Id(target), // amount, // }) - Ok(pallet_fungibles::Call::::transfer { + Ok(fungibles::Call::::transfer { id: Compact(id), target: MultiAddress::Id(target), amount, }) }, - // 22 => { - // let (id, delegate, amount) = - // <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - // .map_err(|_| DispatchError::Other("DecodingFailed"))?; - // Ok(pallet_assets::Call::::approve_transfer { - // id: Compact(id), - // delegate: MultiAddress::Id(delegate), - // amount, - // }) - // }, + 22 => { + let (id, delegate, amount) = + <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) + .map_err(|_| DispatchError::Other("DecodingFailed"))?; + Ok(fungibles::Call::::approve { + id: Compact(id), + spender: MultiAddress::Id(delegate), + value: amount, + }) + // Ok(pallet_assets::Call::::approve_transfer { + // id: Compact(id), + // delegate: MultiAddress::Id(delegate), + // amount, + // }) + }, // other calls _ => Err(DispatchError::Other("UnknownFunctionId")), } diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 822fc24d..ba7ae64f 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -73,6 +73,8 @@ use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; // XCM Imports use xcm::latest::prelude::BodyId; +pub(crate) use pallet_pop_api::fungibles; + /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. pub type AccountId = <::Signer as IdentifyAccount>::AccountId; @@ -321,7 +323,7 @@ impl Contains for AllowedApiCalls { | NftsCall::create_swap { .. } | NftsCall::cancel_swap { .. } | NftsCall::claim_swap { .. } - ) | RuntimeCall::Fungibles(pallet_fungibles::Call::transfer { .. }) + ) | RuntimeCall::Fungibles(fungibles::Call::transfer { .. }) ) } } @@ -663,7 +665,9 @@ construct_runtime!( Nfts: pallet_nfts = 50, NftFractionalization: pallet_nft_fractionalization = 51, Assets: pallet_assets:: = 52, - Fungibles: pallet_fungibles = 53, + + // Pop API + Fungibles: fungibles = 150, } ); From c8d8b2a9c9c3c8f02f5e2bf163f50cd47b94a25d Mon Sep 17 00:00:00 2001 From: Frank Bell Date: Sat, 20 Jul 2024 16:45:35 +0100 Subject: [PATCH 03/56] refactor: rebase main, refactor extension with enums --- Cargo.lock | 32 +- Cargo.toml | 2 +- pallets/api/Cargo.toml | 4 +- pallets/api/src/fungibles.rs | 84 -- pallets/api/src/fungibles/benchmarking.rs | 22 - pallets/api/src/fungibles/mod.rs | 101 +++ .../integration-tests/src/local_fungibles.rs | 7 - pop-api/src/lib.rs | 1 + pop-api/src/v0/assets/mod.rs | 24 +- primitives/src/storage_keys.rs | 26 - runtime/devnet/Cargo.toml | 4 +- .../rustc-ice-2024-07-20T20_16_19-33192.txt | 67 ++ runtime/devnet/src/config/assets.rs | 4 +- runtime/devnet/src/extensions/mod.rs | 765 ++---------------- runtime/devnet/src/extensions/v0/assets.rs | 90 --- runtime/devnet/src/extensions/v0/mod.rs | 1 - runtime/devnet/src/lib.rs | 66 +- runtime/testnet/src/extensions.rs | 554 +------------ runtime/testnet/src/lib.rs | 41 +- 19 files changed, 331 insertions(+), 1564 deletions(-) delete mode 100644 pallets/api/src/fungibles.rs delete mode 100644 pallets/api/src/fungibles/benchmarking.rs create mode 100644 pallets/api/src/fungibles/mod.rs create mode 100644 runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt delete mode 100644 runtime/devnet/src/extensions/v0/assets.rs diff --git a/Cargo.lock b/Cargo.lock index 31848492..a3425194 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6616,6 +6616,21 @@ dependencies = [ "num-traits", ] +[[package]] +name = "pallet-api" +version = "0.1.0" +dependencies = [ + "frame-benchmarking", + "frame-support", + "frame-system", + "pallet-assets", + "parity-scale-codec", + "scale-info", + "sp-core", + "sp-io", + "sp-runtime", +] + [[package]] name = "pallet-asset-conversion" version = "11.0.0" @@ -7537,21 +7552,6 @@ dependencies = [ "sp-std", ] -[[package]] -name = "pallet-pop-api" -version = "0.1.0" -dependencies = [ - "frame-benchmarking", - "frame-support", - "frame-system", - "pallet-assets", - "parity-scale-codec", - "scale-info", - "sp-core", - "sp-io", - "sp-runtime", -] - [[package]] name = "pallet-preimage" version = "29.0.0" @@ -9754,6 +9754,7 @@ dependencies = [ "hex", "hex-literal", "log", + "pallet-api", "pallet-assets", "pallet-aura", "pallet-authorship", @@ -9765,7 +9766,6 @@ dependencies = [ "pallet-nft-fractionalization", "pallet-nfts", "pallet-nfts-runtime-api", - "pallet-pop-api", "pallet-preimage", "pallet-proxy", "pallet-scheduler", diff --git a/Cargo.toml b/Cargo.toml index e727f002..8b2d0a8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -52,7 +52,7 @@ substrate-wasm-builder = "18.0.1" substrate-build-script-utils = "11.0.0" # Local -pallet-pop-api = { path = "pallets/api", default-features = false } +pallet-api = { path = "pallets/api", default-features = false } pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-common = { path = "runtime/common", default-features = false } diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 5973b794..5168ce70 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "pallet-pop-api" +name = "pallet-api" authors.workspace = true -description = "Pop api pallet, enabling smart contracts to use the power of Polkadot" +description = "Api pallet, enabling smart(er) contracts with the power of Polkadot" edition.workspace = true license.workspace = true version = "0.1.0" diff --git a/pallets/api/src/fungibles.rs b/pallets/api/src/fungibles.rs deleted file mode 100644 index 26009a5a..00000000 --- a/pallets/api/src/fungibles.rs +++ /dev/null @@ -1,84 +0,0 @@ -#![cfg_attr(not(feature = "std"), no_std)] - -pub use pallet::*; - -#[cfg(test)] -mod mock; - -#[cfg(test)] -mod tests; - -#[cfg(feature = "runtime-benchmarks")] -mod benchmarking; - -#[frame_support::pallet] -pub mod pallet { - use frame_support::{ - dispatch::WithPostDispatchInfo, pallet_prelude::*, traits::fungibles::Inspect, - }; - use frame_system::pallet_prelude::*; - use pallet_assets::WeightInfo; - use sp_runtime::traits::StaticLookup; - - type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; - type AssetIdOf = as Inspect< - ::AccountId, - >>::AssetId; - type AssetIdParameterOf = - >::AssetIdParameter; - type Assets = pallet_assets::Pallet; - type AssetsWeightInfo = >::WeightInfo; - type BalanceOf = as Inspect< - ::AccountId, - >>::Balance; - // Should be defined in primitives. - type TrustBackedAssetsInstance = pallet_assets::Instance1; - - #[pallet::config] - pub trait Config: - frame_system::Config + pallet_assets::Config - { - } - - #[pallet::pallet] - pub struct Pallet(_); - - #[pallet::call] - impl Pallet { - #[pallet::call_index(0)] - #[pallet::weight(AssetsWeightInfo::::transfer_keep_alive())] - pub fn transfer( - origin: OriginFor, - id: AssetIdParameterOf, - target: AccountIdLookupOf, - amount: BalanceOf, - ) -> DispatchResult { - Assets::::transfer_keep_alive(origin, id, target, amount) - } - - #[pallet::call_index(1)] - #[pallet::weight(AssetsWeightInfo::::cancel_approval())] - pub fn approve( - origin: OriginFor, - id: AssetIdParameterOf, - spender: AccountIdLookupOf, - value: BalanceOf, - ) -> DispatchResultWithPostInfo { - Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) - .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; - Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { - e.with_weight( - AssetsWeightInfo::::cancel_approval() - + AssetsWeightInfo::::approve_transfer(), - ) - })?; - Ok(().into()) - } - } - - impl Pallet { - pub fn total_supply(id: AssetIdOf) -> BalanceOf { - Assets::::total_supply(id) - } - } -} diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs deleted file mode 100644 index f3b0b0ec..00000000 --- a/pallets/api/src/fungibles/benchmarking.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! Benchmarking setup for pallet-parachain-template - -// https://docs.substrate.io/test/benchmark/ - -use super::*; - -#[allow(unused)] -use crate::Pallet as Template; -use frame_benchmarking::{benchmarks, impl_benchmark_test_suite, whitelisted_caller}; -use frame_system::RawOrigin; - -benchmarks! { - do_something { - let s in 0 .. 100; - let caller: T::AccountId = whitelisted_caller(); - }: _(RawOrigin::Signed(caller), s) - verify { - assert_eq!(Something::::get(), Some(s)); - } -} - -impl_benchmark_test_suite!(Template, crate::mock::new_test_ext(), crate::mock::Test,); diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs new file mode 100644 index 00000000..8cf3d031 --- /dev/null +++ b/pallets/api/src/fungibles/mod.rs @@ -0,0 +1,101 @@ +#![cfg_attr(not(feature = "std"), no_std)] + +pub use pallet::*; + +#[cfg(test)] +mod tests; + +#[frame_support::pallet] +pub mod pallet { + use frame_support::{ + dispatch::{DispatchResult, DispatchResultWithPostInfo, WithPostDispatchInfo}, + pallet_prelude::*, + traits::fungibles::Inspect, + }; + use frame_system::pallet_prelude::*; + use pallet_assets::WeightInfo; + use sp_runtime::traits::StaticLookup; + + type AccountIdOf = ::AccountId; + type AssetIdOf = > as Inspect< + ::AccountId, + >>::AssetId; + type AssetIdParameterOf = + >>::AssetIdParameter; + type Assets = pallet_assets::Pallet>; + type AssetsInstanceOf = ::AssetsInstance; + type AssetsWeightInfo = >>::WeightInfo; + type BalanceOf = > as Inspect< + ::AccountId, + >>::Balance; + + /// The required input for state queries in pallet assets. + #[derive(Encode, Decode, Debug, MaxEncodedLen)] + pub enum AssetsKeys { + #[codec(index = 0)] + TotalSupply(AssetIdOf), + #[codec(index = 1)] + BalanceOf(AssetIdOf, AccountIdOf), + #[codec(index = 2)] + Allowance(AssetIdOf, AccountIdOf, AccountIdOf), + #[codec(index = 3)] + TokenName(AssetIdOf), + #[codec(index = 4)] + TokenSymbol(AssetIdOf), + #[codec(index = 5)] + TokenDecimals(AssetIdOf), + } + + #[pallet::config] + pub trait Config: frame_system::Config + pallet_assets::Config { + type AssetsInstance; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::call] + impl Pallet { + #[pallet::call_index(9)] + #[pallet::weight(AssetsWeightInfo::::transfer_keep_alive())] + pub fn transfer( + origin: OriginFor, + id: AssetIdOf, + target: AccountIdOf, + amount: BalanceOf, + ) -> DispatchResult { + let target = T::Lookup::unlookup(target); + Assets::::transfer_keep_alive(origin, id.into(), target, amount) + } + + #[pallet::call_index(10)] + #[pallet::weight(AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] + pub fn approve( + origin: OriginFor, + id: AssetIdOf, + spender: AccountIdOf, + value: BalanceOf, + ) -> DispatchResultWithPostInfo { + let spender = T::Lookup::unlookup(spender); + let id: AssetIdParameterOf = id.into(); + Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; + Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { + e.with_weight( + AssetsWeightInfo::::cancel_approval() + + AssetsWeightInfo::::approve_transfer(), + ) + })?; + Ok(().into()) + } + } + + impl Pallet { + pub fn total_supply(id: AssetIdOf) -> BalanceOf { + Assets::::total_supply(id) + } + pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { + Assets::::balance(id, owner) + } + } +} diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 8f0384c0..a316f6ea 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -263,7 +263,6 @@ fn token_decimals_asset(asset_id: AssetId) -> u8 { /// - decrease_allowance #[test] -#[ignore] fn total_supply_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); @@ -285,7 +284,6 @@ fn total_supply_works() { } #[test] -#[ignore] fn balance_of_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); @@ -304,7 +302,6 @@ fn balance_of_works() { } #[test] -#[ignore] fn allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); @@ -329,7 +326,6 @@ fn allowance_works() { } #[test] -#[ignore] fn transfer_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); @@ -382,7 +378,6 @@ fn transfer_works() { } #[test] -#[ignore] fn transfer_from_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); @@ -435,7 +430,6 @@ fn transfer_from_works() { } #[test] -#[ignore] fn increase_allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); @@ -493,7 +487,6 @@ fn increase_allowance_works() { /// - token_decimals #[test] -#[ignore] fn token_metadata_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index 86621c51..87d8a3f0 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -30,6 +30,7 @@ mod constants { // Modules: pub(crate) const ASSETS: u8 = 52; + pub(crate) const FUNGIBLES: u8 = 150; pub(crate) const BALANCES: u8 = 10; } diff --git a/pop-api/src/v0/assets/mod.rs b/pop-api/src/v0/assets/mod.rs index 00021fc0..a5ec6416 100644 --- a/pop-api/src/v0/assets/mod.rs +++ b/pop-api/src/v0/assets/mod.rs @@ -1,7 +1,7 @@ use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; use crate::{ - constants::{ASSETS, DECODING_FAILED, DISPATCH, READ_STATE}, + constants::{ASSETS, DECODING_FAILED, DISPATCH, FUNGIBLES, READ_STATE}, primitives::{AccountId, AssetId, Balance}, v0::V0, Result, StatusCode, @@ -23,11 +23,11 @@ pub mod fungibles; /// - burn /// - transfer /// - transfer_keep_alive -const TRANSFER_KEEP_ALIVE: u8 = 9; +const TRANSFER: u8 = 9; /// - set_metadata /// - clear_metadata /// - approve_transfer -const APPROVE_TRANSFER: u8 = 22; +const APPROVE: u8 = 10; /// - cancel_approval const CANCEL_APPROVAL: u8 = 23; /// - transfer_approved @@ -91,17 +91,11 @@ const TRANSFER_APPROVED: u8 = 25; /// Move some assets from the sender account to another, keeping the sender account alive. #[inline] pub fn transfer_keep_alive(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([ - V0, - DISPATCH, - ASSETS, - // E.D. is always respected with transferring tokens via the API. - TRANSFER_KEEP_ALIVE, - ])) - .input::<(AssetId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, target, amount)) + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, target, amount)) } // /// Set the metadata for an asset. @@ -122,7 +116,7 @@ pub fn transfer_keep_alive(id: AssetId, target: AccountId, amount: Balance) -> R /// Approve an amount of asset for transfer by a delegated third-party account. #[inline] pub fn approve_transfer(id: AssetId, delegate: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, ASSETS, APPROVE_TRANSFER])) + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, APPROVE])) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() diff --git a/primitives/src/storage_keys.rs b/primitives/src/storage_keys.rs index e42dbca0..15be3953 100644 --- a/primitives/src/storage_keys.rs +++ b/primitives/src/storage_keys.rs @@ -2,19 +2,6 @@ use super::nfts::*; use super::*; -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum RuntimeStateKeys { - #[cfg(feature = "cross-chain")] - #[codec(index = 1)] - ParachainSystem(ParachainSystemKeys), - #[cfg(feature = "nfts")] - #[codec(index = 50)] - Nfts(NftsKeys), - #[cfg(feature = "assets")] - #[codec(index = 52)] - Assets(AssetsKeys), -} - #[cfg(feature = "cross-chain")] #[derive(Encode, Decode, Debug, MaxEncodedLen)] pub enum ParachainSystemKeys { @@ -41,16 +28,3 @@ pub enum NftsKeys { /// Get the attribute value of `item` of `collection` corresponding to `key`. CollectionAttribute(CollectionId, BoundedVec), } - -/// The required input for state queries in pallet assets. -#[cfg(feature = "assets")] -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum AssetsKeys { - TotalSupply(AssetId), - BalanceOf(AssetId, AccountId), - Allowance(AssetId, AccountId, AccountId), - TokenName(AssetId), - TokenSymbol(AssetId), - TokenDecimals(AssetId), - // AssetExists(AssetId), -} diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index c4a1fe7c..875640e2 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -24,7 +24,7 @@ smallvec.workspace = true # Local pop-primitives = { workspace = true, features = ["assets", "cross-chain", "nfts"] } pop-runtime-common.workspace = true -pallet-pop-api.workspace = true +pallet-api.workspace = true # Substrate frame-benchmarking.workspace = true @@ -120,7 +120,7 @@ std = [ "pallet-balances/std", "pallet-collator-selection/std", "pallet-contracts/std", - "pallet-pop-api/std", + "pallet-api/std", "pallet-message-queue/std", "pallet-multisig/std", "pallet-nft-fractionalization/std", diff --git a/runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt b/runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt new file mode 100644 index 00000000..af09c6e9 --- /dev/null +++ b/runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt @@ -0,0 +1,67 @@ +thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: +Box +stack backtrace: + 0: 0x101b39474 - std::backtrace::Backtrace::create::h307d17d5fb2cc0d0 + 1: 0x10ae51494 - as core[f61c09f58d048be3]::ops::function::Fn<(&dyn for<'a, 'b> core[f61c09f58d048be3]::ops::function::Fn<(&'a core[f61c09f58d048be3]::panic::panic_info::PanicInfo<'b>,), Output = ()> + core[f61c09f58d048be3]::marker::Send + core[f61c09f58d048be3]::marker::Sync, &core[f61c09f58d048be3]::panic::panic_info::PanicInfo)>>::call + 2: 0x101b52074 - std::panicking::rust_panic_with_hook::h84760468187ddc85 + 3: 0x10aeb0e28 - std[73ec08d302f978e7]::panicking::begin_panic::::{closure#0} + 4: 0x10aeb0ddc - std[73ec08d302f978e7]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> + 5: 0x10ee0858c - std[73ec08d302f978e7]::panicking::begin_panic:: + 6: 0x10aecc478 - ::emit_producing_guarantee + 7: 0x10b906c3c - rustc_middle[7d6fe3bd18d758b7]::util::bug::opt_span_bug_fmt::::{closure#0} + 8: 0x10b8ff0f4 - rustc_middle[7d6fe3bd18d758b7]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} + 9: 0x10b8ff0c0 - rustc_middle[7d6fe3bd18d758b7]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> + 10: 0x10ee9d3cc - rustc_middle[7d6fe3bd18d758b7]::util::bug::bug_fmt + 11: 0x10b8fcb68 - >::fold_ty + 12: 0x10bef26fc - ::ctor_sub_tys + 13: 0x10bef95d4 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_exhaustiveness_and_usefulness:: + 14: 0x10bef9e28 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_exhaustiveness_and_usefulness:: + 15: 0x10bef9e28 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_exhaustiveness_and_usefulness:: + 16: 0x10befc130 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_match_usefulness:: + 17: 0x10bf19bc4 - rustc_pattern_analysis[1c40ec00ea6b64ed]::analyze_match + 18: 0x10b9e79a8 - ::analyze_patterns + 19: 0x10b9ec200 - ::visit_expr + 20: 0x10b9ec0e0 - ::visit_expr + 21: 0x10b9ec49c - ::visit_expr + 22: 0x10b9ec0e0 - ::visit_expr + 23: 0x10b9e6a00 - rustc_mir_build[8d95155ec7b238f2]::thir::pattern::check_match::check_match + 24: 0x10c017140 - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> + 25: 0x10c0d1e98 - >::call_once + 26: 0x10bfc8334 - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, true, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> + 27: 0x10c0fcdd8 - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace + 28: 0x10b9850d0 - rustc_mir_build[8d95155ec7b238f2]::build::mir_built + 29: 0x10c01c100 - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> + 30: 0x10c094e28 - >::call_once + 31: 0x10bfcc0b4 - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> + 32: 0x10c0ef058 - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace + 33: 0x10ba1d4d4 - rustc_mir_build[8d95155ec7b238f2]::check_unsafety::check_unsafety + 34: 0x10c0179a4 - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> + 35: 0x10c059dfc - >::call_once + 36: 0x10bfc4b3c - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> + 37: 0x10c0f6d54 - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace + 38: 0x10b5b83fc - std[73ec08d302f978e7]::panicking::try::<(), core[f61c09f58d048be3]::panic::unwind_safe::AssertUnwindSafe::par_body_owners::{closure#0}>::{closure#0}::{closure#0}::{closure#0}>> + 39: 0x10b56c9f8 - rustc_data_structures[ff694425d12ac56b]::sync::parallel::disabled::par_for_each_in::<&[rustc_span[8def9120bc3fdd6f]::def_id::LocalDefId], ::par_body_owners::{closure#0}> + 40: 0x10b5b6448 - ::time::<(), rustc_interface[2352a4bab4f6367d]::passes::analysis::{closure#1}> + 41: 0x10b5614fc - rustc_interface[2352a4bab4f6367d]::passes::analysis + 42: 0x10c01bdac - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> + 43: 0x10c0d47d0 - >::call_once + 44: 0x10bf83e54 - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> + 45: 0x10c0ebcec - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace + 46: 0x10ae149f0 - ::enter::> + 47: 0x10ae13c18 - ::enter::, rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>> + 48: 0x10adffe80 - rustc_span[8def9120bc3fdd6f]::create_session_globals_then::, rustc_interface[2352a4bab4f6367d]::interface::run_compiler, rustc_driver_impl[e71547a46cb79a13]::run_compiler::{closure#0}>::{closure#0}> + 49: 0x10ae1182c - std[73ec08d302f978e7]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[e71547a46cb79a13]::run_compiler::{closure#0}>::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>> + 50: 0x10ae08230 - <::spawn_unchecked_, rustc_driver_impl[e71547a46cb79a13]::run_compiler::{closure#0}>::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>>::{closure#1} as core[f61c09f58d048be3]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} + 51: 0x101b5a5a8 - std::sys::pal::unix::thread::Thread::new::thread_start::h9266fbbdd0c3d8be + 52: 0x1898def94 - __pthread_joiner_wake + + +rustc version: 1.78.0 (9b00956e5 2024-04-29) +platform: aarch64-apple-darwin + +query stack during panic: +#0 [check_match] match-checking `::contains` +#1 [mir_built] building MIR for `::contains` +#2 [check_unsafety] unsafety-checking `::contains` +#3 [analysis] running analysis passes on this crate +end of query stack diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 5c506a41..26874710 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -121,4 +121,6 @@ impl pallet_assets::Config for Runtime { type BenchmarkHelper = (); } -impl fungibles::Config for Runtime {} +impl fungibles::Config for Runtime { + type AssetsInstance = TrustBackedAssetsInstance; +} diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 7bbe7954..a2076eae 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -1,22 +1,17 @@ use codec::{Compact, Decode, Encode}; -use cumulus_pallet_parachain_system::RelaychainDataProvider; use frame_support::traits::{Contains, OriginTrait}; use frame_support::{ dispatch::{GetDispatchInfo, RawOrigin}, pallet_prelude::*, - traits::{ - fungibles::{approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect}, - nonfungibles_v2::Inspect as NonFungiblesInspect, + traits::fungibles::{ + approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, }, }; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, }; use sp_core::crypto::UncheckedFrom; -use sp_runtime::{ - traits::{BlockNumberProvider, Dispatchable}, - DispatchError, MultiAddress, -}; +use sp_runtime::{traits::Dispatchable, DispatchError, MultiAddress}; use sp_std::{boxed::Box, vec::Vec}; use xcm::{ latest::{prelude::*, OriginKind::SovereignAccount}, @@ -24,16 +19,17 @@ use xcm::{ }; use crate::{ - config::assets::TrustBackedAssetsInstance, fungibles, AccountId, AllowedApiCalls, Balance, - Runtime, RuntimeCall, RuntimeOrigin, UNIT, + config::assets::TrustBackedAssetsInstance, + fungibles::{ + self, + AssetsKeys::{self, *}, + }, + state_keys::RuntimeStateKeys, + AccountId, AllowedApiCalls, Balance, Runtime, RuntimeCall, RuntimeOrigin, UNIT, }; use pop_primitives::{ cross_chain::CrossChainMessage, nfts::{CollectionId, ItemId}, - storage_keys::{ - AssetsKeys::{self, *}, - NftsKeys, ParachainSystemKeys, RuntimeStateKeys, - }, AssetId, }; @@ -54,6 +50,7 @@ where T: pallet_contracts::Config + pallet_xcm::Config + pallet_assets::Config + + fungibles::Config + pallet_nfts::Config + cumulus_pallet_parachain_system::Config + frame_system::Config< @@ -121,7 +118,7 @@ fn dispatch( version: u8, pallet_index: u8, call_index: u8, - params: Vec, + mut params: Vec, ) -> Result<(), DispatchError> where T: frame_system::Config, @@ -129,10 +126,20 @@ where E: Ext, { const LOG_PREFIX: &str = " dispatch |"; - let call = construct_call(version, pallet_index, call_index, params)?; + + // Prefix params with version, pallet, index to simplify decoding + params.insert(0, version); + params.insert(1, pallet_index); + params.insert(2, call_index); + + let call = ::decode(&mut ¶ms[..]) + .map_err(|_| DispatchError::Other("DecodingFailed"))?; + // Contract is the origin by default. let origin: RuntimeOrigin = RawOrigin::Signed(env.ext().address().clone()).into(); - dispatch_call::(env, call, origin, LOG_PREFIX) + match call { + VersionedRuntimeCall::V0(call) => dispatch_call::(env, call, origin, LOG_PREFIX), + } } fn dispatch_call( @@ -165,80 +172,36 @@ where } } -fn construct_call( - version: u8, - pallet_index: u8, - call_index: u8, - params: Vec, -) -> Result { - match pallet_index { - index if index == super::Assets::index() as u8 => { - let call = versioned_construct_assets_call(version, call_index, params)?; - Ok(RuntimeCall::Fungibles(call)) - }, - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -fn construct_key( - version: u8, - pallet_index: u8, - call_index: u8, - params: Vec, -) -> Result { - match pallet_index { - 52 => { - let key = versioned_construct_assets_key(version, call_index, params)?; - Ok(RuntimeStateKeys::Assets(key)) - }, - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -fn versioned_construct_assets_call( - version: u8, - call_index: u8, - params: Vec, - // ) -> Result, DispatchError> { -) -> Result, DispatchError> { - match version { - V0 => v0::assets::construct_assets_call(call_index, params), - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -fn versioned_construct_assets_key( - version: u8, - call_index: u8, - params: Vec, -) -> Result { - match version { - V0 => v0::assets::construct_assets_key(call_index, params), - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - fn read_state( env: &mut Environment, version: u8, pallet_index: u8, call_index: u8, - params: Vec, + mut params: Vec, ) -> Result<(), DispatchError> where T: pallet_contracts::Config + pallet_assets::Config + + fungibles::Config + pallet_nfts::Config + cumulus_pallet_parachain_system::Config + frame_system::Config, E: Ext, { const LOG_PREFIX: &str = " read_state |"; - let key = construct_key(version, pallet_index, call_index, params)?; + + // Prefix params with version, pallet, index to simplify decoding + params.insert(0, version); + params.insert(1, pallet_index); + params.insert(2, call_index); + + let key = >::decode(&mut ¶ms[..]) + .map_err(|_| DispatchError::Other("DecodingFailed"))?; + let result = match key { - RuntimeStateKeys::Nfts(key) => read_nfts_state::(key, env), - RuntimeStateKeys::ParachainSystem(key) => read_parachain_system_state::(key, env), - RuntimeStateKeys::Assets(key) => read_assets_state::(key, env), + VersionedRuntimeStateKeys::V0(key) => match key { + RuntimeStateKeys::Assets(key) => read_assets_state::(key, env), + }, }? .encode(); log::trace!( @@ -248,6 +211,20 @@ where env.write(&result, false, None) } +// Example wrapper to enable versioning of state read keys +#[derive(Encode, Decode, Debug, MaxEncodedLen)] +enum VersionedRuntimeStateKeys { + #[codec(index = 0)] + V0(RuntimeStateKeys), +} + +// Example wrapper to enable versioning of state read keys +#[derive(Decode, Debug)] +enum VersionedRuntimeCall { + #[codec(index = 0)] + V0(RuntimeCall), +} + fn send_xcm(env: &mut Environment) -> Result<(), DispatchError> where T: pallet_contracts::Config @@ -374,118 +351,52 @@ impl TryFrom for FuncId { } } -fn read_parachain_system_state( - key: ParachainSystemKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + cumulus_pallet_parachain_system::Config, - E: Ext, -{ - match key { - ParachainSystemKeys::LastRelayChainBlockNumber => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(RelaychainDataProvider::::current_block_number().encode()) - }, - } -} - -fn read_nfts_state( - key: NftsKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + pallet_nfts::Config, - E: Ext, -{ - match key { - NftsKeys::Collection(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Collection::::get(collection).encode()) - }, - NftsKeys::CollectionOwner(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_owner(collection).encode()) - }, - NftsKeys::Item(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Item::::get(collection, item).encode()) - }, - NftsKeys::Owner(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::owner(collection, item).encode()) - }, - NftsKeys::Attribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::attribute(&collection, &item, &key).encode()) - }, - // NftsKeys::CustomAttribute(account, collection, item, key) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key) - // .encode()) - // }, - NftsKeys::SystemAttribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::system_attribute(&collection, item.as_ref(), &key) - .encode()) - }, - NftsKeys::CollectionAttribute(collection, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_attribute(&collection, &key).encode()) - }, - } -} - fn read_assets_state( - key: AssetsKeys, + key: AssetsKeys, env: &mut Environment, ) -> Result, DispatchError> where T: pallet_contracts::Config - + pallet_assets::Config, + + pallet_assets::Config + + fungibles::Config, E: Ext, T: frame_system::Config, { + env.charge_weight(T::DbWeight::get().reads(1_u64))?; match key { - TotalSupply(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_assets::Pallet::::total_supply(id).encode()) - }, - BalanceOf(id, owner) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_assets::Pallet::::balance(id, &owner.0.into()) - .encode()) - }, - Allowance(id, owner, spender) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_assets::Pallet::::allowance( - id, - &owner.0.into(), - &spender.0.into(), - ) - .encode()) - }, - TokenName(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok( as MetadataInspect< - AccountId, - >>::name(id) - .encode()) - }, - TokenSymbol(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok( as MetadataInspect< - AccountId, - >>::symbol(id) - .encode()) - }, - TokenDecimals(id) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok( as MetadataInspect< - AccountId, - >>::decimals(id) - .encode()) - }, + TotalSupply(id) => Ok(fungibles::Pallet::::total_supply(id).encode()), + BalanceOf(id, owner) => Ok(fungibles::Pallet::::balance_of(id, &owner).encode()), + _ => todo!(), + // Allowance(id, owner, spender) => { + // env.charge_weight(T::DbWeight::get().reads(1_u64))?; + // Ok(pallet_assets::Pallet::::allowance( + // id, + // &owner.0.into(), + // &spender.0.into(), + // ) + // .encode()) + // }, + // TokenName(id) => { + // env.charge_weight(T::DbWeight::get().reads(1_u64))?; + // Ok( as MetadataInspect< + // AccountId, + // >>::name(id) + // .encode()) + // }, + // TokenSymbol(id) => { + // env.charge_weight(T::DbWeight::get().reads(1_u64))?; + // Ok( as MetadataInspect< + // AccountId, + // >>::symbol(id) + // .encode()) + // }, + // TokenDecimals(id) => { + // env.charge_weight(T::DbWeight::get().reads(1_u64))?; + // Ok( as MetadataInspect< + // AccountId, + // >>::decimals(id) + // .encode()) + // }, // AssetsKeys::AssetExists(id) => { // env.charge_weight(T::DbWeight::get().reads(1_u64))?; // Ok(pallet_assets::Pallet::::asset_exists(id).encode()) @@ -674,501 +585,3 @@ mod tests { }); } } -// use enumflags2::BitFlags; -// use pallet_nfts::{CollectionConfig, CollectionSetting, CollectionSettings, MintSettings}; -// use parachains_common::CollectionId; -// { -// // NFT helper functions -// fn collection_config_from_disabled_settings( -// settings: BitFlags, -// ) -> CollectionConfig { -// CollectionConfig { -// settings: CollectionSettings::from_disabled(settings), -// max_supply: None, -// mint_settings: MintSettings::default(), -// } -// } -// -// fn default_collection_config() -> CollectionConfig { -// collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()) -// } -// -// #[test] -// #[ignore] -// fn dispatch_balance_transfer_from_contract_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/balance-transfer/target/ink/balance_transfer.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("transfer_through_runtime"); -// let value_to_send: u128 = 10 * UNIT; -// let params = [function, BOB.encode(), value_to_send.encode()].concat(); -// -// let bob_balance_before = Balances::free_balance(&BOB); -// assert_eq!(bob_balance_before, INITIAL_AMOUNT); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// -// let bob_balance_after = Balances::free_balance(&BOB); -// assert_eq!(bob_balance_before + value_to_send, bob_balance_after); -// }); -// } -// -// // Create a test for tesing create_nft_collection -// #[test] -// #[ignore] -// fn dispatch_nfts_create_nft_collection() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/nfts/target/ink/pop_api_nft_example.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("create_nft_collection"); -// -// let params = [function].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check that the nft collection was created -// assert_eq!(Nfts::collection_owner(0), Some(addr.clone().into())); -// -// // test reading the collection -// let function = function_selector("read_collection"); -// -// let params = [function, 0.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // assert that the collection was read successfully -// assert_eq!(result.result.clone().unwrap().data, vec![1, 1]); -// }); -// } -// -// #[test] -// #[ignore] -// fn dispatch_nfts_mint_from_contract_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = -// load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") -// .unwrap(); -// -// let init_value = 100; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let collection_id: u32 = 0; -// let item_id: u32 = 1; -// -// // create nft collection with contract as owner -// assert_eq!( -// Nfts::force_create( -// RuntimeOrigin::root(), -// addr.clone().into(), -// default_collection_config() -// ), -// Ok(()) -// ); -// -// assert_eq!(Nfts::collection_owner(collection_id), Some(addr.clone().into())); -// // assert that the item does not exist yet -// assert_eq!(Nfts::owner(collection_id, item_id), None); -// -// let function = function_selector("mint_through_runtime"); -// -// let params = -// [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// -// assert_eq!(Nfts::owner(collection_id, item_id), Some(BOB.into())); -// }); -// } -// -// #[test] -// #[ignore] -// fn nfts_mint_surfaces_error() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = -// load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") -// .unwrap(); -// -// let init_value = 100; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let collection_id: u32 = 0; -// let item_id: u32 = 1; -// -// let function = function_selector("mint_through_runtime"); -// -// let params = -// [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert with expected error -// let result = result.result.unwrap(); -// assert!(result.did_revert()); -// }); -// } -// -// #[test] -// #[ignore] -// fn reading_last_relay_chain_block_number_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/read-runtime-state/target/ink/read_relay_blocknumber.wasm", -// ) -// .unwrap(); -// -// let init_value = 100; -// -// let contract = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!contract.result.did_revert(), "deploying contract reverted {:?}", contract); -// -// let addr = contract.account_id; -// -// let function = function_selector("read_relay_block_number"); -// let params = [function].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::UnsafeCollect, -// pallet_contracts::Determinism::Relaxed, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// }); -// } -// -// #[test] -// #[ignore] -// fn place_spot_order_from_contract_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../pop-api/examples/place-spot-order/target/ink/spot_order.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("place_spot_order"); -// -// let max_amount = 1 * UNIT; -// let para_id = 2000; -// -// let params = [function, max_amount.encode(), para_id.encode()].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// }); -// } -// -// #[test] -// #[ignore] -// fn allow_call_filter_blocks_call() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// -// let (wasm_binary, _) = load_wasm_module::( -// "../../tests/contracts/filtered-call/target/ink/pop_api_filtered_call.wasm", -// ) -// .unwrap(); -// -// let init_value = 100 * UNIT; -// -// let result = Contracts::bare_instantiate( -// ALICE, -// init_value, -// GAS_LIMIT, -// None, -// Code::Upload(wasm_binary), -// function_selector("new"), -// vec![], -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// ) -// .result -// .unwrap(); -// -// assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); -// -// let addr = result.account_id; -// -// let function = function_selector("get_filtered"); -// let params = [function].concat(); -// -// let result = Contracts::bare_call( -// ALICE, -// addr.clone(), -// 0, -// Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), -// None, -// params, -// DEBUG_OUTPUT, -// pallet_contracts::CollectEvents::Skip, -// pallet_contracts::Determinism::Enforced, -// ); -// -// if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { -// log::debug!( -// "Contract debug buffer - {:?}", -// String::from_utf8(result.debug_message.clone()) -// ); -// log::debug!("filtered result: {:?}", result); -// } -// -// // check for revert -// assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); -// }); -// } -// } diff --git a/runtime/devnet/src/extensions/v0/assets.rs b/runtime/devnet/src/extensions/v0/assets.rs deleted file mode 100644 index d385028d..00000000 --- a/runtime/devnet/src/extensions/v0/assets.rs +++ /dev/null @@ -1,90 +0,0 @@ -use crate::{ - extensions::{ - AccountId as AccountId32, AssetId, - AssetsKeys::{self, *}, - Balance, Compact, Decode, DispatchError, MultiAddress, Runtime, TrustBackedAssetsInstance, - }, - fungibles, -}; -use pop_primitives::AccountId; -use sp_std::vec::Vec; - -pub(crate) fn construct_assets_key( - call_index: u8, - params: Vec, -) -> Result { - match call_index { - 0 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TotalSupply(id)) - }, - 1 => { - let (id, owner) = <(AssetId, AccountId)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(BalanceOf(id, owner)) - }, - 2 => { - let (id, owner, spender) = <(AssetId, AccountId, AccountId)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(Allowance(id, owner, spender)) - }, - 3 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenName(id)) - }, - 4 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenSymbol(id)) - }, - 5 => { - let id = ::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(TokenDecimals(id)) - }, - // other calls - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} - -pub(crate) fn construct_assets_call( - call_index: u8, - params: Vec, - // ) -> Result, DispatchError> { -) -> Result, DispatchError> { - match call_index { - 9 => { - let (id, target, amount) = <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - // Ok(pallet_assets::Call::::transfer_keep_alive { - // id: Compact(id), - // target: MultiAddress::Id(target), - // amount, - // }) - Ok(fungibles::Call::::transfer { - id: Compact(id), - target: MultiAddress::Id(target), - amount, - }) - }, - 22 => { - let (id, delegate, amount) = - <(AssetId, AccountId32, Balance)>::decode(&mut ¶ms[..]) - .map_err(|_| DispatchError::Other("DecodingFailed"))?; - Ok(fungibles::Call::::approve { - id: Compact(id), - spender: MultiAddress::Id(delegate), - value: amount, - }) - // Ok(pallet_assets::Call::::approve_transfer { - // id: Compact(id), - // delegate: MultiAddress::Id(delegate), - // amount, - // }) - }, - // other calls - _ => Err(DispatchError::Other("UnknownFunctionId")), - } -} diff --git a/runtime/devnet/src/extensions/v0/mod.rs b/runtime/devnet/src/extensions/v0/mod.rs index 6406e08f..cf0627b1 100644 --- a/runtime/devnet/src/extensions/v0/mod.rs +++ b/runtime/devnet/src/extensions/v0/mod.rs @@ -1,2 +1 @@ -pub(crate) mod assets; pub(crate) mod error; diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index ba7ae64f..f6042d55 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -73,7 +73,7 @@ use weights::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight}; // XCM Imports use xcm::latest::prelude::BodyId; -pub(crate) use pallet_pop_api::fungibles; +pub(crate) use pallet_api::fungibles; /// Some way of identifying an account on the chain. We intentionally make it equivalent /// to the public key of our transaction signing scheme. @@ -290,40 +290,7 @@ impl Contains for AllowedApiCalls { | AssetsCall::touch_other { .. } | AssetsCall::refund_other { .. } | AssetsCall::block { .. } - ) | RuntimeCall::Nfts( - NftsCall::create { .. } - | NftsCall::destroy { .. } - | NftsCall::mint { .. } - | NftsCall::burn { .. } - | NftsCall::transfer { .. } - | NftsCall::redeposit { .. } - | NftsCall::lock_item_transfer { .. } - | NftsCall::unlock_item_transfer { .. } - | NftsCall::lock_collection { .. } - | NftsCall::transfer_ownership { .. } - | NftsCall::set_team { .. } - | NftsCall::approve_transfer { .. } - | NftsCall::cancel_approval { .. } - | NftsCall::clear_all_transfer_approvals { .. } - | NftsCall::lock_item_properties { .. } - | NftsCall::set_attribute { .. } - | NftsCall::clear_attribute { .. } - | NftsCall::approve_item_attributes { .. } - | NftsCall::cancel_item_attributes_approval { .. } - | NftsCall::set_metadata { .. } - | NftsCall::clear_metadata { .. } - | NftsCall::set_collection_metadata { .. } - | NftsCall::clear_collection_metadata { .. } - | NftsCall::set_accept_ownership { .. } - | NftsCall::set_collection_max_supply { .. } - | NftsCall::update_mint_settings { .. } - | NftsCall::set_price { .. } - | NftsCall::buy_item { .. } - | NftsCall::pay_tips { .. } - | NftsCall::create_swap { .. } - | NftsCall::cancel_swap { .. } - | NftsCall::claim_swap { .. } - ) | RuntimeCall::Fungibles(fungibles::Call::transfer { .. }) + ) | RuntimeCall::Fungibles(fungibles::Call::transfer { .. }) ) } } @@ -1035,3 +1002,32 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } + +pub(crate) mod state_keys { + use super::{fungibles, Runtime}; + use codec::{Decode, Encode, MaxEncodedLen}; + + #[derive(Encode, Decode, Debug, MaxEncodedLen)] + pub enum RuntimeStateKeys { + #[codec(index = 52)] + Assets(fungibles::AssetsKeys), + } +} + +#[test] +fn check_encoding() { + use codec::{Compact, Encode}; + use sp_runtime::{AccountId32, MultiAddress}; + + let id = Compact(5u32); + let spender = MultiAddress::Id(AccountId32::from([0; 32])); + let value = 100u128; + let encoded_runtime_call = RuntimeCall::Fungibles(fungibles::Call::::approve { + id, + spender: spender.clone(), + value, + }) + .encode(); + let encoded = (150u8, 10u8, id, spender, value).encode(); + assert_eq!(encoded_runtime_call, encoded); +} diff --git a/runtime/testnet/src/extensions.rs b/runtime/testnet/src/extensions.rs index bcb0f835..d2d11598 100644 --- a/runtime/testnet/src/extensions.rs +++ b/runtime/testnet/src/extensions.rs @@ -1,22 +1,14 @@ -use cumulus_pallet_parachain_system::RelaychainDataProvider; +use frame_support::traits::{Contains, OriginTrait}; use frame_support::{ dispatch::{GetDispatchInfo, RawOrigin}, pallet_prelude::*, - traits::nonfungibles_v2::Inspect, - traits::{Contains, OriginTrait}, }; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, ChargedAmount, Environment, Ext, InitState, RetVal, }; -use pop_primitives::{ - nfts::{CollectionId, ItemId}, - storage_keys::{NftsKeys, ParachainSystemKeys, RuntimeStateKeys}, -}; +use pop_primitives::storage_keys::RuntimeStateKeys; use sp_core::crypto::UncheckedFrom; -use sp_runtime::{ - traits::{BlockNumberProvider, Dispatchable}, - DispatchError, -}; +use sp_runtime::{traits::Dispatchable, DispatchError}; use sp_std::vec::Vec; use crate::{AccountId, AllowedApiCalls, RuntimeCall, RuntimeOrigin}; @@ -31,9 +23,6 @@ pub struct PopApiExtension; impl ChainExtension for PopApiExtension where T: pallet_contracts::Config - + pallet_xcm::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config + frame_system::Config< RuntimeOrigin = RuntimeOrigin, AccountId = AccountId, @@ -44,7 +33,6 @@ where fn call(&mut self, env: Environment) -> Result where E: Ext, - T::AccountId: UncheckedFrom + AsRef<[u8]>, { log::debug!(target:LOG_TARGET, " extension called "); match v0::FuncId::try_from(env.func_id())? { @@ -101,8 +89,6 @@ fn dispatch_call( log_prefix: &str, ) -> Result<(), DispatchError> where - T: frame_system::Config, - RuntimeOrigin: From>, E: Ext, { let charged_dispatch_weight = env.charge_weight(call.get_dispatch_info().weight)?; @@ -157,8 +143,7 @@ where fn dispatch(env: Environment) -> Result<(), DispatchError> where - T: pallet_contracts::Config - + frame_system::Config, + T: pallet_contracts::Config, RuntimeOrigin: From>, E: Ext, { @@ -180,10 +165,7 @@ where fn read_state(env: Environment) -> Result<(), DispatchError> where - T: pallet_contracts::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config - + frame_system::Config, + T: pallet_contracts::Config, E: Ext, { const LOG_PREFIX: &str = " read_state |"; @@ -202,12 +184,8 @@ where let key: RuntimeStateKeys = env.read_as()?; let result = match key { - RuntimeStateKeys::Nfts(key) => read_nfts_state::(key, &mut env), - RuntimeStateKeys::ParachainSystem(key) => { - read_parachain_system_state::(key, &mut env) - }, - _ => Ok(Vec::default()), - }? + _ => Vec::::default(), + } .encode(); log::trace!( @@ -219,521 +197,3 @@ where DispatchError::Other("unable to write results to contract memory") }) } - -fn read_parachain_system_state( - key: ParachainSystemKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + cumulus_pallet_parachain_system::Config, - E: Ext, -{ - match key { - ParachainSystemKeys::LastRelayChainBlockNumber => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(RelaychainDataProvider::::current_block_number().encode()) - }, - } -} - -fn read_nfts_state( - key: NftsKeys, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config + pallet_nfts::Config, - E: Ext, -{ - match key { - NftsKeys::Collection(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Collection::::get(collection).encode()) - }, - NftsKeys::CollectionOwner(collection) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_owner(collection).encode()) - }, - NftsKeys::Item(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Item::::get(collection, item).encode()) - }, - NftsKeys::Owner(collection, item) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::owner(collection, item).encode()) - }, - NftsKeys::Attribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::attribute(&collection, &item, &key).encode()) - }, - // NftsKeys::CustomAttribute(account, collection, item, key) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_nfts::Pallet::::custom_attribute(&account, &collection, &item, &key) - // .encode()) - // }, - NftsKeys::SystemAttribute(collection, item, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::system_attribute(&collection, item.as_ref(), &key) - .encode()) - }, - NftsKeys::CollectionAttribute(collection, key) => { - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - Ok(pallet_nfts::Pallet::::collection_attribute(&collection, &key).encode()) - }, - } -} - -#[cfg(test)] -mod tests { - pub use super::*; - pub use crate::*; - use enumflags2::BitFlags; - pub use pallet_contracts::Code; - use pallet_nfts::{CollectionConfig, CollectionSetting, CollectionSettings, MintSettings}; - use parachains_common::CollectionId; - pub use sp_runtime::{traits::Hash, AccountId32}; - - const DEBUG_OUTPUT: pallet_contracts::DebugInfo = pallet_contracts::DebugInfo::UnsafeDebug; - - const ALICE: AccountId32 = AccountId32::new([1_u8; 32]); - const BOB: AccountId32 = AccountId32::new([2_u8; 32]); - const INITIAL_AMOUNT: u128 = 100_000 * UNIT; - const GAS_LIMIT: Weight = Weight::from_parts(100_000_000_000, 3 * 1024 * 1024); - - fn new_test_ext() -> sp_io::TestExternalities { - let mut t = frame_system::GenesisConfig::::default() - .build_storage() - .expect("Frame system builds valid default genesis config"); - - pallet_balances::GenesisConfig:: { - balances: vec![(ALICE, INITIAL_AMOUNT), (BOB, INITIAL_AMOUNT)], - } - .assimilate_storage(&mut t) - .expect("Pallet balances storage can be assimilated"); - - let mut ext = sp_io::TestExternalities::new(t); - ext.execute_with(|| System::set_block_number(1)); - 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() - } - - // NFT helper functions - fn collection_config_from_disabled_settings( - settings: BitFlags, - ) -> CollectionConfig { - CollectionConfig { - settings: CollectionSettings::from_disabled(settings), - max_supply: None, - mint_settings: MintSettings::default(), - } - } - - fn default_collection_config() -> CollectionConfig { - collection_config_from_disabled_settings(CollectionSetting::DepositRequired.into()) - } - - #[test] - #[ignore] - fn dispatch_balance_transfer_from_contract_works() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../pop-api/examples/balance-transfer/target/ink/balance_transfer.wasm", - ) - .unwrap(); - - let init_value = 100 * UNIT; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let function = function_selector("transfer_through_runtime"); - let value_to_send: u128 = 10 * UNIT; - let params = [function, BOB.encode(), value_to_send.encode()].concat(); - - let bob_balance_before = Balances::free_balance(&BOB); - assert_eq!(bob_balance_before, INITIAL_AMOUNT); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - - let bob_balance_after = Balances::free_balance(&BOB); - assert_eq!(bob_balance_before + value_to_send, bob_balance_after); - }); - } - - #[test] - #[ignore] - fn dispatch_nfts_mint_from_contract_works() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = - load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") - .unwrap(); - - let init_value = 100; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let collection_id: u32 = 0; - let item_id: u32 = 1; - - // create nft collection with contract as owner - assert_eq!( - Nfts::force_create( - RuntimeOrigin::root(), - addr.clone().into(), - default_collection_config() - ), - Ok(()) - ); - - assert_eq!(Nfts::collection_owner(collection_id), Some(addr.clone().into())); - // assert that the item does not exist yet - assert_eq!(Nfts::owner(collection_id, item_id), None); - - let function = function_selector("mint_through_runtime"); - - let params = - [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - - assert_eq!(Nfts::owner(collection_id, item_id), Some(BOB.into())); - }); - } - - #[test] - #[ignore] - fn nfts_mint_surfaces_error() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = - load_wasm_module::("../../pop-api/examples/nfts/target/ink/nfts.wasm") - .unwrap(); - - let init_value = 100; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let collection_id: u32 = 0; - let item_id: u32 = 1; - - let function = function_selector("mint_through_runtime"); - - let params = - [function, collection_id.encode(), item_id.encode(), BOB.encode()].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert with expected error - let result = result.result.unwrap(); - assert!(result.did_revert()); - }); - } - - #[test] - #[ignore] - fn reading_last_relay_chain_block_number_works() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../pop-api/examples/read-runtime-state/target/ink/read_relay_blocknumber.wasm", - ) - .unwrap(); - - let init_value = 100; - - let contract = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!contract.result.did_revert(), "deploying contract reverted {:?}", contract); - - let addr = contract.account_id; - - let function = function_selector("read_relay_block_number"); - let params = [function].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::UnsafeCollect, - pallet_contracts::Determinism::Relaxed, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - }); - } - - #[test] - #[ignore] - fn place_spot_order_from_contract_fails() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../pop-api/examples/place-spot-order/target/ink/spot_order.wasm", - ) - .unwrap(); - - let init_value = 100 * UNIT; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let function = function_selector("place_spot_order"); - - let max_amount = 1 * UNIT; - let para_id = 2000; - - let params = [function, max_amount.encode(), para_id.encode()].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("result: {:?}", result); - } - - // check for revert - assert!( - result.result.is_err(), - "Contract execution should have failed - unimplemented runtime call!" - ); - }); - } - - #[test] - #[ignore] - fn allow_call_filter_blocks_call() { - new_test_ext().execute_with(|| { - let _ = env_logger::try_init(); - - let (wasm_binary, _) = load_wasm_module::( - "../../tests/contracts/filtered-call/target/ink/pop_api_filtered_call.wasm", - ) - .unwrap(); - - let init_value = 100 * UNIT; - - let result = Contracts::bare_instantiate( - ALICE, - init_value, - GAS_LIMIT, - None, - Code::Upload(wasm_binary), - function_selector("new"), - vec![], - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - ) - .result - .unwrap(); - - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - - let addr = result.account_id; - - let function = function_selector("get_filtered"); - let params = [function].concat(); - - let result = Contracts::bare_call( - ALICE, - addr.clone(), - 0, - Weight::from_parts(100_000_000_000, 3 * 1024 * 1024), - None, - params, - DEBUG_OUTPUT, - pallet_contracts::CollectEvents::Skip, - pallet_contracts::Determinism::Enforced, - ); - - if DEBUG_OUTPUT == pallet_contracts::DebugInfo::UnsafeDebug { - log::debug!( - "Contract debug buffer - {:?}", - String::from_utf8(result.debug_message.clone()) - ); - log::debug!("filtered result: {:?}", result); - } - - // check for revert - assert!(!result.result.unwrap().did_revert(), "Contract reverted!"); - }); - } -} diff --git a/runtime/testnet/src/lib.rs b/runtime/testnet/src/lib.rs index c4178011..5573ef18 100644 --- a/runtime/testnet/src/lib.rs +++ b/runtime/testnet/src/lib.rs @@ -252,45 +252,8 @@ impl Contains for FilteredCalls { /// A type to identify allowed calls to the Runtime from contracts. Used by Pop API pub struct AllowedApiCalls; impl Contains for AllowedApiCalls { - fn contains(c: &RuntimeCall) -> bool { - use pallet_nfts::Call as NftsCall; - matches!( - c, - RuntimeCall::Balances(BalancesCall::transfer_keep_alive { .. }) - | RuntimeCall::Nfts( - NftsCall::create { .. } - | NftsCall::destroy { .. } - | NftsCall::mint { .. } | NftsCall::burn { .. } - | NftsCall::transfer { .. } - | NftsCall::redeposit { .. } - | NftsCall::lock_item_transfer { .. } - | NftsCall::unlock_item_transfer { .. } - | NftsCall::lock_collection { .. } - | NftsCall::transfer_ownership { .. } - | NftsCall::set_team { .. } - | NftsCall::approve_transfer { .. } - | NftsCall::cancel_approval { .. } - | NftsCall::clear_all_transfer_approvals { .. } - | NftsCall::lock_item_properties { .. } - | NftsCall::set_attribute { .. } - | NftsCall::clear_attribute { .. } - | NftsCall::approve_item_attributes { .. } - | NftsCall::cancel_item_attributes_approval { .. } - | NftsCall::set_metadata { .. } - | NftsCall::clear_metadata { .. } - | NftsCall::set_collection_metadata { .. } - | NftsCall::clear_collection_metadata { .. } - | NftsCall::set_accept_ownership { .. } - | NftsCall::set_collection_max_supply { .. } - | NftsCall::update_mint_settings { .. } - | NftsCall::set_price { .. } - | NftsCall::buy_item { .. } - | NftsCall::pay_tips { .. } - | NftsCall::create_swap { .. } - | NftsCall::cancel_swap { .. } - | NftsCall::claim_swap { .. } - ) - ) + fn contains(_c: &RuntimeCall) -> bool { + false } } From c88387d2d31443a3e146a2273fa3993719d8fd04 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Sun, 21 Jul 2024 15:01:43 +0200 Subject: [PATCH 04/56] refactor --- Cargo.lock | 23 +- Cargo.toml | 2 +- pallets/api/Cargo.toml | 2 + pallets/api/src/fungibles/mod.rs | 49 +- pop-api/Cargo.toml | 11 +- pop-api/examples/fungibles/Cargo.toml | 2 +- pop-api/integration-tests/Cargo.toml | 4 +- .../contracts/fungibles/Cargo.toml | 21 + .../contracts/fungibles/lib.rs | 185 ++++ .../integration-tests/src/local_fungibles.rs | 42 +- pop-api/src/lib.rs | 10 +- pop-api/src/primitives.rs | 4 +- pop-api/src/v0/assets/fungibles.rs | 369 ++++---- pop-api/src/v0/assets/mod.rs | 229 +---- pop-api/src/v0/balances.rs | 88 -- pop-api/src/v0/cross_chain/coretime.rs | 11 - pop-api/src/v0/cross_chain/mod.rs | 107 --- pop-api/src/v0/mod.rs | 7 - pop-api/src/v0/nfts.rs | 883 ------------------ primitives/Cargo.toml | 6 +- primitives/src/cross_chain.rs | 19 - primitives/src/lib.rs | 29 +- primitives/src/storage_keys.rs | 30 - runtime/devnet/Cargo.toml | 4 +- runtime/devnet/src/extensions/mod.rs | 121 +-- runtime/devnet/src/lib.rs | 45 +- runtime/testnet/Cargo.toml | 6 +- 27 files changed, 532 insertions(+), 1777 deletions(-) create mode 100755 pop-api/integration-tests/contracts/fungibles/Cargo.toml create mode 100755 pop-api/integration-tests/contracts/fungibles/lib.rs delete mode 100644 pop-api/src/v0/balances.rs delete mode 100644 pop-api/src/v0/cross_chain/coretime.rs delete mode 100644 pop-api/src/v0/cross_chain/mod.rs delete mode 100644 pop-api/src/v0/nfts.rs delete mode 100644 primitives/src/cross_chain.rs delete mode 100644 primitives/src/storage_keys.rs diff --git a/Cargo.lock b/Cargo.lock index a3425194..3b200f43 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6625,6 +6625,7 @@ dependencies = [ "frame-system", "pallet-assets", "parity-scale-codec", + "primitives", "scale-info", "sp-core", "sp-io", @@ -9708,15 +9709,6 @@ dependencies = [ "substrate-prometheus-endpoint", ] -[[package]] -name = "pop-primitives" -version = "0.0.0" -dependencies = [ - "bounded-collections 0.1.9", - "parity-scale-codec", - "scale-info", -] - [[package]] name = "pop-runtime-common" version = "0.0.0" @@ -9780,8 +9772,8 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-runtime-common", - "pop-primitives", "pop-runtime-common", + "primitives", "rand", "scale-info", "smallvec", @@ -9854,8 +9846,8 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-runtime-common", - "pop-primitives", "pop-runtime-common", + "primitives", "scale-info", "smallvec", "sp-api", @@ -9961,6 +9953,15 @@ dependencies = [ "uint", ] +[[package]] +name = "primitives" +version = "0.0.0" +dependencies = [ + "bounded-collections 0.1.9", + "parity-scale-codec", + "scale-info", +] + [[package]] name = "prioritized-metered-channel" version = "0.6.1" diff --git a/Cargo.toml b/Cargo.toml index 8b2d0a8f..79bf5ebe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ pallet-api = { path = "pallets/api", default-features = false } pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-common = { path = "runtime/common", default-features = false } -pop-primitives = { path = "./primitives", default-features = false } +primitives = { path = "./primitives", default-features = false } # Substrate sc-basic-authorship = "0.35.0" diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 5168ce70..0e87e744 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -18,6 +18,7 @@ frame-benchmarking.workspace = true frame-support.workspace = true frame-system.workspace = true pallet-assets.workspace = true +primitives = { workspace = true, features = ["fungibles"] } sp-runtime.workspace = true [dev-dependencies] @@ -38,6 +39,7 @@ std = [ "frame-support/std", "frame-system/std", "pallet-assets/std", + "primitives/std", "scale-info/std", "sp-core/std", "sp-io/std", diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 8cf3d031..3f5f1742 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -10,12 +10,16 @@ pub mod pallet { use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo, WithPostDispatchInfo}, pallet_prelude::*, - traits::fungibles::Inspect, + traits::fungibles::{ + approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, Inspect, + }, }; use frame_system::pallet_prelude::*; use pallet_assets::WeightInfo; use sp_runtime::traits::StaticLookup; + use primitives::constants::fungibles::*; + type AccountIdOf = ::AccountId; type AssetIdOf = > as Inspect< ::AccountId, @@ -31,19 +35,15 @@ pub mod pallet { /// The required input for state queries in pallet assets. #[derive(Encode, Decode, Debug, MaxEncodedLen)] - pub enum AssetsKeys { - #[codec(index = 0)] - TotalSupply(AssetIdOf), - #[codec(index = 1)] - BalanceOf(AssetIdOf, AccountIdOf), - #[codec(index = 2)] - Allowance(AssetIdOf, AccountIdOf, AccountIdOf), - #[codec(index = 3)] - TokenName(AssetIdOf), - #[codec(index = 4)] - TokenSymbol(AssetIdOf), - #[codec(index = 5)] - TokenDecimals(AssetIdOf), + #[repr(u8)] + #[allow(clippy::unnecessary_cast)] + pub enum Keys { + TotalSupply(AssetIdOf) = TOTAL_SUPPLY, + BalanceOf(AssetIdOf, AccountIdOf) = BALANCE_OF, + Allowance(AssetIdOf, AccountIdOf, AccountIdOf) = ALLOWANCE, + TokenName(AssetIdOf) = TOKEN_NAME, + TokenSymbol(AssetIdOf) = TOKEN_SYMBOL, + TokenDecimals(AssetIdOf) = TOKEN_DECIMALS, } #[pallet::config] @@ -94,8 +94,29 @@ pub mod pallet { pub fn total_supply(id: AssetIdOf) -> BalanceOf { Assets::::total_supply(id) } + pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { Assets::::balance(id, owner) } + + pub fn allowance( + id: AssetIdOf, + owner: &AccountIdOf, + spender: &AccountIdOf, + ) -> BalanceOf { + Assets::::allowance(id, owner, spender) + } + + pub fn token_name(id: AssetIdOf) -> Vec { + as MetadataInspect>>::name(id) + } + + pub fn token_symbol(id: AssetIdOf) -> Vec { + as MetadataInspect>>::symbol(id) + } + + pub fn token_decimals(id: AssetIdOf) -> u8 { + as MetadataInspect>>::decimals(id) + } } } diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index dc48ea8a..2a4784e0 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "pop-api" -description = "Easily access the power of Polkadot via the Pop Network" +description = "Enabling smart(er) contracts with the power of Polkadot" license = "GPL-3.0-only" version = "0.0.0" edition = "2021" @@ -10,7 +10,7 @@ enumflags2 = { version = "0.7.7" } ink = { version = "5.0.0", default-features = false } sp-io = { version = "31.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } -pop-primitives = { path = "../primitives", default-features = false } +primitives = { path = "../primitives", default-features = false } [lib] name = "pop_api" @@ -22,10 +22,7 @@ default = ["std"] std = [ "enumflags2/std", "ink/std", - "pop-primitives/std", + "primitives/std", "sp-io/std", ] -assets = ["pop-primitives/assets"] -balances = [] -nfts = ["pop-primitives/nfts"] -cross-chain = ["pop-primitives/cross-chain"] +fungibles = ["primitives/fungibles"] diff --git a/pop-api/examples/fungibles/Cargo.toml b/pop-api/examples/fungibles/Cargo.toml index 565b0554..19d9ce12 100755 --- a/pop-api/examples/fungibles/Cargo.toml +++ b/pop-api/examples/fungibles/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" [dependencies] ink = { version = "5.0.0", default-features = false } -pop-api = { path = "../../../pop-api", default-features = false, features = ["assets"] } +pop-api = { path = "../../../pop-api", default-features = false, features = ["fungibles"] } [lib] path = "lib.rs" diff --git a/pop-api/integration-tests/Cargo.toml b/pop-api/integration-tests/Cargo.toml index 94c0ba83..52b3b634 100644 --- a/pop-api/integration-tests/Cargo.toml +++ b/pop-api/integration-tests/Cargo.toml @@ -11,7 +11,7 @@ frame-system = { version = "29.0.0", default-features = false } pallet-balances = { version = "29.0.2", default-features = false } pallet-assets = { version = "30.0.0", default-features = false } pallet-contracts = { version = "28.0.0", default-features = false } -pop-primitives = { path = "../../primitives", default-features = false, features = ["assets"] } +primitives = { path = "../../primitives", default-features = false } pop-runtime-devnet = { path = "../../runtime/devnet", default-features = false } sp-io = { version = "31.0.0", default-features = false } sp-runtime = { version = "32.0.0", default-features = false } @@ -25,7 +25,7 @@ std = [ "pallet-balances/std", "pallet-assets/std", "pallet-contracts/std", - "pop-primitives/std", + "primitives/std", "pop-runtime-devnet/std", "scale/std", "sp-io/std", diff --git a/pop-api/integration-tests/contracts/fungibles/Cargo.toml b/pop-api/integration-tests/contracts/fungibles/Cargo.toml new file mode 100755 index 00000000..7c322004 --- /dev/null +++ b/pop-api/integration-tests/contracts/fungibles/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "fungibles" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "5.0.0", default-features = false } +pop-api = { path = "../../../../pop-api", default-features = false, features = ["fungibles"] } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "pop-api/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs new file mode 100755 index 00000000..1b42fec4 --- /dev/null +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -0,0 +1,185 @@ +#![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, + StatusCode, +}; + +pub type Result = core::result::Result; + +#[ink::contract] +mod fungibles { + use super::*; + + #[ink(storage)] + #[derive(Default)] + pub struct 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) + } + + #[ink(message)] + pub fn balance_of(&self, id: AssetId, owner: AccountId) -> Result { + api::balance_of(id, owner) + } + + #[ink(message)] + pub fn allowance( + &self, + id: AssetId, + owner: AccountId, + spender: AccountId, + ) -> Result { + api::allowance(id, owner, spender) + } + + #[ink(message)] + pub fn transfer(&self, id: AssetId, to: AccountId, value: Balance) -> Result<()> { + api::transfer(id, to, value) + } + + #[ink(message)] + pub fn transfer_from( + &self, + id: AssetId, + 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) + } + + #[ink(message)] + pub fn approve(&self, id: AssetId, spender: AccountId, value: Balance) -> Result<()> { + api::approve(id, spender, value) + } + + #[ink(message)] + pub fn increase_allowance( + &self, + id: AssetId, + spender: AccountId, + value: Balance, + ) -> Result<()> { + api::increase_allowance(id, spender, value) + } + + #[ink(message)] + pub fn decrease_allowance( + &self, + id: AssetId, + spender: AccountId, + value: Balance, + ) -> Result<()> { + api::decrease_allowance(id, 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) + } + + #[ink(message)] + pub fn token_symbol(&self, id: AssetId) -> Result> { + api::token_symbol(id) + } + + #[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(); + } + } +} diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index a316f6ea..a559fb30 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -1,5 +1,5 @@ use super::*; -use pop_primitives::error::{ +use primitives::error::{ ArithmeticError::*, Error::{self, *}, TokenError::*, @@ -266,11 +266,7 @@ fn token_decimals_asset(asset_id: AssetId) -> u8 { fn total_supply_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate( - "../../pop-api/examples/fungibles/target/ink/fungibles.wasm", - INIT_VALUE, - vec![], - ); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!(Assets::total_supply(ASSET_ID), total_supply(addr.clone(), ASSET_ID)); @@ -287,8 +283,7 @@ fn total_supply_works() { fn balance_of_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!(Assets::balance(ASSET_ID, BOB), balance_of(addr.clone(), ASSET_ID, BOB)); @@ -305,8 +300,7 @@ fn balance_of_works() { fn allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!( @@ -329,8 +323,7 @@ fn allowance_works() { fn transfer_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. @@ -381,8 +374,7 @@ fn transfer_works() { fn transfer_from_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. @@ -433,7 +425,7 @@ fn transfer_from_works() { fn increase_allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("../examples/fungibles/target/ink/fungibles.wasm", 0, vec![]); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![]); let amount: Balance = 100 * UNIT; let asset = 0; create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); @@ -443,7 +435,7 @@ fn increase_allowance_works() { ); let addr = - instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); + instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); // Asset does not exist. let asset = 1; assert_eq!( @@ -490,11 +482,7 @@ fn increase_allowance_works() { fn token_metadata_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate( - "../../pop-api/examples/fungibles/target/ink/fungibles.wasm", - INIT_VALUE, - vec![], - ); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); let name: Vec = vec![11, 12, 13]; let symbol: Vec = vec![21, 22, 23]; @@ -529,7 +517,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); // // // No tokens in circulation. // assert_eq!(Assets::asset_exists(ASSET_ID), asset_exists(addr.clone(), ASSET_ID)); @@ -546,7 +534,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); // let amount: Balance = 100 * UNIT; // // // Asset does not exist. @@ -606,14 +594,14 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // // Instantiate a contract without balance (relay token). -// let addr = instantiate("../examples/fungibles/target/ink/fungibles.wasm", 0, vec![0]); +// let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![0]); // // No balance to pay for fees. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), // Module { index: 10, error: 2 }, // ); // // Instantiate a contract without balance (relay token). -// let addr = instantiate("../examples/fungibles/target/ink/fungibles.wasm", 100, vec![2]); +// let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 100, vec![2]); // // No balance to pay the deposit. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), @@ -621,7 +609,7 @@ fn token_metadata_works() { // ); // // Instantiate a contract with balance. // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); +// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, BOB, 0)), // Module { index: 52, error: 7 }, @@ -645,7 +633,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("../examples/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); // // create_asset(addr.clone(), ASSET_ID, 1); // let result = set_metadata(addr.clone(), ASSET_ID, vec![12], vec![12], 12); diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index 87d8a3f0..7bb33555 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -4,14 +4,7 @@ use ink::env::chain_extension::FromStatusCode; use constants::DECODING_FAILED; -#[cfg(feature = "assets")] pub use v0::assets; -#[cfg(feature = "balances")] -pub use v0::balances; -#[cfg(feature = "cross-chain")] -pub use v0::cross_chain; -#[cfg(feature = "nfts")] -pub use v0::nfts; pub mod primitives; pub mod v0; @@ -22,7 +15,7 @@ pub type Result = core::result::Result; mod constants { // Errors: pub(crate) const DECODING_FAILED: u32 = 255; - pub(crate) const MODULE_ERROR: u8 = 3; + pub(crate) const _MODULE_ERROR: u8 = 3; // Function IDs: pub(crate) const DISPATCH: u8 = 0; @@ -30,7 +23,6 @@ mod constants { // Modules: pub(crate) const ASSETS: u8 = 52; - pub(crate) const FUNGIBLES: u8 = 150; pub(crate) const BALANCES: u8 = 10; } diff --git a/pop-api/src/primitives.rs b/pop-api/src/primitives.rs index 33285044..f0429b3f 100644 --- a/pop-api/src/primitives.rs +++ b/pop-api/src/primitives.rs @@ -1,8 +1,6 @@ use ink::env::{DefaultEnvironment, Environment}; -pub use pop_primitives::*; +pub use primitives::*; pub(crate) type AccountId = ::AccountId; pub(crate) type Balance = ::Balance; -#[cfg(any(feature = "nfts", feature = "cross-chain"))] -type BlockNumber = ::BlockNumber; diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 255e8502..0a8ea8ab 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -1,26 +1,107 @@ -use ink::prelude::vec::Vec; +use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; use crate::{ - assets, - constants::{ASSETS, BALANCES, MODULE_ERROR}, + constants::{ASSETS, BALANCES, DECODING_FAILED, DISPATCH, READ_STATE}, primitives::{AccountId, AssetId, Balance}, + v0::V0, Result, StatusCode, }; +use constants::*; +pub use metadata::*; +use primitives::constants::{fungibles::*, FUNGIBLES}; /// Local Fungibles: /// 1. PSP-22 Interface /// 2. PSP-22 Metadata Interface /// 3. Asset Management -/// 1. PSP-22 Interface: -/// - total_supply -/// - balance_of -/// - allowance -/// - transfer -/// - transfer_from -/// - approve -/// - increase_allowance -/// - decrease_allowance +pub(super) mod constants { + /// 1. PSP-22 Interface: + /// - total_supply + /// - balance_of + /// - allowance + /// - transfer + pub(super) const TRANSFER: u8 = 9; + /// - transfer_from + pub(super) const TRANSFER_FROM: u8 = 9; + /// - approve + pub(super) const APPROVE: u8 = 10; + /// - increase_allowance + pub(super) const _INCREASE_ALLOWANCE: u8 = 10; + /// - decrease_allowance + pub(super) const _DECREASE_ALLOWANCE: u8 = 10; + + // 3. Asset Management: + // - create + // - start_destroy + // - destroy_accounts + // - destroy_approvals + // - finish_destroy + // - set_metadata + // - clear_metadata +} + +/// Represents various errors related to local fungible assets in the Pop API. +/// +/// The `FungiblesError` provides a detailed and specific set of error types that can occur when +/// interacting with fungible assets through the Pop API. Each variant signifies a particular error +/// condition, facilitating precise error handling and debugging. +/// +/// It is designed to be lightweight, including only the essential errors relevant to fungible asset +/// operations. The `Other` variant serves as a catch-all for any unexpected errors. For more +/// detailed debugging, the `Other` variant can be converted into the richer `Error` type defined in +/// the primitives crate. +#[derive(Debug, PartialEq, Eq)] +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub enum FungiblesError { + /// An unspecified or unknown error occurred. + Other(StatusCode), + /// The asset is not live; either frozen or being destroyed. + AssetNotLive, + /// Not enough allowance to fulfill a request is available. + InsufficientAllowance, + /// Not enough balance to fulfill a request is available. + InsufficientBalance, + /// The asset ID is already taken. + InUse, + /// Minimum balance should be non-zero. + MinBalanceZero, + /// The account to alter does not exist. + NoAccount, + /// The signing account has no permission to do the operation. + NoPermission, + /// The given asset ID is unknown. + Unknown, + /// No balance for creation of assets or fees. + // TODO: Originally `pallet_balances::Error::InsufficientBalance` but collides with the + // `InsufficientBalance` error that is used for `pallet_assets::Error::BalanceLow` to adhere to + // standard. This deserves a second look. + NoBalance, +} + +impl From for FungiblesError { + /// Converts a `StatusCode` to a `FungiblesError`. + /// + /// This conversion maps a `StatusCode`, returned by the runtime, to a more descriptive + /// `FungiblesError`. This provides better context and understanding of the error, allowing + /// developers to handle the most important errors effectively. + fn from(value: StatusCode) -> Self { + let encoded = value.0.to_le_bytes(); + match encoded { + // Balances. + [_, BALANCES, 2, _] => FungiblesError::NoBalance, + // Assets. + [_, ASSETS, 0, _] => FungiblesError::NoAccount, + [_, ASSETS, 1, _] => FungiblesError::NoPermission, + [_, ASSETS, 2, _] => FungiblesError::Unknown, + [_, ASSETS, 3, _] => FungiblesError::InUse, + [_, ASSETS, 5, _] => FungiblesError::MinBalanceZero, + [_, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, + [_, ASSETS, 10, _] => FungiblesError::AssetNotLive, + _ => FungiblesError::Other(value), + } + } +} /// Returns the total token supply for a given asset ID. /// @@ -31,7 +112,12 @@ use crate::{ /// The total supply of the token, or an error if the operation fails. #[inline] pub fn total_supply(id: AssetId) -> Result { - assets::total_supply(id) + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOTAL_SUPPLY])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if @@ -45,7 +131,12 @@ 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 { - assets::balance_of(id, owner) + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, BALANCE_OF])) + .input::<(AssetId, AccountId)>() + .output::>, true>() + .handle_error_code::() + .call(&(id, owner)) + .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given @@ -60,7 +151,12 @@ 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 { - assets::allowance(id, owner, spender) + ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, ALLOWANCE])) + .input::<(AssetId, AccountId, AccountId)>() + .output::>, true>() + .handle_error_code::() + .call(&(id, owner, spender)) + .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional @@ -74,8 +170,12 @@ pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result Result<()> { - assets::transfer_keep_alive(id, to, value) +pub fn transfer(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, target, amount)) } /// Transfers `value` tokens on behalf of `from` to account `to` with additional `data` @@ -91,8 +191,12 @@ pub fn transfer(id: AssetId, to: AccountId, value: Balance) -> Result<()> { /// # Returns /// Returns `Ok(())` if successful, or an error if the transfer fails. #[inline] -pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, value: Balance) -> Result<()> { - assets::transfer_approved(id, from, to, value) +pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER_FROM])) + .input::<(AssetId, AccountId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, from, to, amount)) } /// Approves an account to spend a specified number of tokens on behalf of the caller. @@ -105,9 +209,12 @@ pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, value: Balance /// # Returns /// Returns `Ok(())` if successful, or an error if the approval fails. #[inline] -pub fn approve(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - assets::cancel_approval(id, spender)?; - assets::approve_transfer(id, spender, value) +pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, APPROVE])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, spender, amount)) } /// Increases the allowance of a spender. @@ -120,8 +227,8 @@ pub fn approve(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] -pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { - assets::approve_transfer(id, spender, value) +pub fn increase_allowance(_id: AssetId, _spender: AccountId, _value: Balance) -> Result<()> { + Ok(()) } /// Decreases the allowance of a spender. @@ -141,56 +248,61 @@ pub fn decrease_allowance(_id: AssetId, _spender: AccountId, _value: Balance) -> Ok(()) } -/// 2. PSP-22 Metadata Interface: -/// - token_name -/// - token_symbol -/// - token_decimals - -/// Returns the token name for a given asset ID. -/// -/// # Arguments -/// * `id` - The ID of the asset. -/// -/// # Returns -/// The name of the token as a byte vector, or an error if the operation fails. -#[inline] -pub fn token_name(id: AssetId) -> Result> { - assets::token_name(id) -} +pub mod metadata { + use super::*; + /// Returns the token name for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// 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])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) + } -/// Returns the token symbol for a given asset ID. -/// -/// # Arguments -/// * `id` - The ID of the asset. -/// -/// # Returns -/// The symbol of the token as a byte vector, or an error if the operation fails. -#[inline] -pub fn token_symbol(id: AssetId) -> Result> { - assets::token_symbol(id) -} + /// Returns the token symbol for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// 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])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) + } -/// Returns the token decimals for a given asset ID. -/// -/// # Arguments -/// * `id` - The ID of the asset. -/// -/// # Returns -/// 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 { - assets::token_decimals(id) + /// Returns the token decimals for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// 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])) + .input::() + .output::>, true>() + .handle_error_code::() + .call(&(id)) + .and_then(|v| ::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) + } } -// /// 3. Asset Management: -// /// - create -// /// - start_destroy -// /// - destroy_accounts -// /// - destroy_approvals -// /// - finish_destroy -// /// - set_metadata -// /// - clear_metadata -// +// pub asset_management { // /// Create a new token with a given asset ID. // /// // /// # Arguments @@ -200,9 +312,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the creation fails. -// // pub fn create(id: AssetId, admin: impl Into>, min_balance: Balance) -> Result<()> { -// // assets::create(id, admin, min_balance) -// // } +// pub fn create(id: AssetId, admin: impl Into>, min_balance: Balance) -> Result<()> { +// Ok(()) +// } // // /// Start the process of destroying a token with a given asset ID. // /// @@ -211,11 +323,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn start_destroy(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::StartDestroy { -// // id: id.into(), -// // }))?) -// // } +// fn start_destroy(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Destroy all accounts associated with a token with a given asset ID. // /// @@ -224,11 +334,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn destroy_accounts(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::DestroyAccounts { -// // id: id.into(), -// // }))?) -// // } +// fn destroy_accounts(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Destroy all approvals associated with a token with a given asset ID. // /// @@ -237,11 +345,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn destroy_approvals(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::DestroyApprovals { -// // id: id.into(), -// // }))?) -// // } +// fn destroy_approvals(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Complete the process of destroying a token with a given asset ID. // /// @@ -250,11 +356,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn finish_destroy(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::FinishDestroy { -// // id: id.into(), -// // }))?) -// // } +// fn finish_destroy(id: AssetId) -> Result<()> { +// Ok(()) +// } // // /// Set the metadata for a token with a given asset ID. // /// @@ -263,9 +367,9 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // pub fn set_metadata(id: AssetId, name: Vec, symbol: Vec, decimals: u8) -> Result<()> { -// // assets::set_metadata(id, name, symbol, decimals) -// // } +// pub fn set_metadata(id: AssetId, name: Vec, symbol: Vec, decimals: u8) -> Result<()> { +// Ok(()) +// } // // /// Clear the metadata for a token with a given asset ID. // /// @@ -274,78 +378,15 @@ pub fn token_decimals(id: AssetId) -> Result { // /// // /// # Returns // /// Returns `Ok(())` if successful, or an error if the operation fails. -// // fn clear_metadata(id: AssetId) -> Result<()> { -// // Ok(dispatch(RuntimeCall::Assets(AssetsCall::ClearMetadata { -// // id: id.into(), -// // }))?) -// // } +// fn clear_metadata(id: AssetId) -> Result<()> { +// Ok(()) +// } +// } // // pub fn asset_exists(id: AssetId) -> Result { // assets::asset_exists(id) // } -/// Represents various errors related to local fungible assets in the Pop API. -/// -/// The `FungiblesError` provides a detailed and specific set of error types that can occur when -/// interacting with fungible assets through the Pop API. Each variant signifies a particular error -/// condition, facilitating precise error handling and debugging. -/// -/// It is designed to be lightweight, including only the essential errors relevant to fungible asset -/// operations. The `Other` variant serves as a catch-all for any unexpected errors. For more -/// detailed debugging, the `Other` variant can be converted into the richer `Error` type defined in -/// the primitives crate. -#[derive(Debug, PartialEq, Eq)] -#[ink::scale_derive(Encode, Decode, TypeInfo)] -pub enum FungiblesError { - /// An unspecified or unknown error occurred. - Other(StatusCode), - /// The asset is not live; either frozen or being destroyed. - AssetNotLive, - /// Not enough allowance to fulfill a request is available. - InsufficientAllowance, - /// Not enough balance to fulfill a request is available. - InsufficientBalance, - /// The asset ID is already taken. - InUse, - /// Minimum balance should be non-zero. - MinBalanceZero, - /// The account to alter does not exist. - NoAccount, - /// The signing account has no permission to do the operation. - NoPermission, - /// The given asset ID is unknown. - Unknown, - /// No balance for creation of assets or fees. - // TODO: Originally `pallet_balances::Error::InsufficientBalance` but collides with the - // `InsufficientBalance` error that is used for `pallet_assets::Error::BalanceLow` to adhere to - // standard. This deserves a second look. - NoBalance, -} - -impl From for FungiblesError { - /// Converts a `StatusCode` to a `FungiblesError`. - /// - /// This conversion maps a `StatusCode`, returned by the runtime, to a more descriptive - /// `FungiblesError`. This provides better context and understanding of the error, allowing - /// developers to handle the most important errors effectively. - fn from(value: StatusCode) -> Self { - let encoded = value.0.to_le_bytes(); - match encoded { - // Balances. - [MODULE_ERROR, BALANCES, 2, _] => FungiblesError::NoBalance, - // Assets. - [MODULE_ERROR, ASSETS, 0, _] => FungiblesError::NoAccount, - [MODULE_ERROR, ASSETS, 1, _] => FungiblesError::NoPermission, - [MODULE_ERROR, ASSETS, 2, _] => FungiblesError::Unknown, - [MODULE_ERROR, ASSETS, 3, _] => FungiblesError::InUse, - [MODULE_ERROR, ASSETS, 5, _] => FungiblesError::MinBalanceZero, - [MODULE_ERROR, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, - [MODULE_ERROR, ASSETS, 10, _] => FungiblesError::AssetNotLive, - _ => FungiblesError::Other(value), - } - } -} - #[cfg(test)] mod tests { use ink::scale::{Decode, Encode}; @@ -407,7 +448,7 @@ mod tests { Corruption, Unavailable, RootNotAllowed, - UnknownFunctionCall, + UnknownCall, DecodingFailed, ]; for error in other_errors { diff --git a/pop-api/src/v0/assets/mod.rs b/pop-api/src/v0/assets/mod.rs index a5ec6416..197db710 100644 --- a/pop-api/src/v0/assets/mod.rs +++ b/pop-api/src/v0/assets/mod.rs @@ -1,229 +1,2 @@ -use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; - -use crate::{ - constants::{ASSETS, DECODING_FAILED, DISPATCH, FUNGIBLES, READ_STATE}, - primitives::{AccountId, AssetId, Balance}, - v0::V0, - Result, StatusCode, -}; - +#[cfg(feature = "fungibles")] pub mod fungibles; - -/// [Pallet Assets](https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/assets/src/lib.rs): -/// 1. Dispatchables -/// 2. Read state functions -/// -/// 1. Dispatchables within pallet assets (TrustBackedAssets instance): -/// - create -/// - start_destroy -/// - destroy_accounts -/// - destroy_approvals -/// - finish_destroy -/// - mint -/// - burn -/// - transfer -/// - transfer_keep_alive -const TRANSFER: u8 = 9; -/// - set_metadata -/// - clear_metadata -/// - approve_transfer -const APPROVE: u8 = 10; -/// - cancel_approval -const CANCEL_APPROVAL: u8 = 23; -/// - transfer_approved -const TRANSFER_APPROVED: u8 = 25; - -/// Issue a new class of fungible assets from a public origin. -// pub(crate) fn create( -// id: AssetId, -// admin: impl Into>, -// min_balance: Balance, -// ) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::Create { -// id: id.into(), -// admin: admin.into(), -// min_balance, -// })) -// } -// -// /// Start the process of destroying a fungible asset class. -// pub(crate) fn start_destroy(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::StartDestroy { id: id.into() })) -// } -// -// /// Destroy all accounts associated with a given asset. -// pub(crate) fn destroy_accounts(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::DestroyAccounts { id: id.into() })) -// } -// -// /// Destroy all approvals associated with a given asset up to the max (see runtime configuration Assets `RemoveItemsLimit`). -// pub(crate) fn destroy_approvals(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::DestroyApprovals { id: id.into() })) -// } -// -// /// Complete destroying asset and unreserve currency. -// pub(crate) fn finish_destroy(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::FinishDestroy { id: id.into() })) -// } - -// /// Mint assets of a particular class. -// pub(crate) fn mint( -// id: AssetId, -// beneficiary: impl Into>, -// amount: Balance, -// ) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::Mint { -// id: id.into(), -// beneficiary: beneficiary.into(), -// amount: Compact(amount), -// })) -// } -// -// /// Reduce the balance of `who` by as much as possible up to `amount` assets of `id`. -// pub(crate) fn burn(id: AssetId, who: impl Into>, amount: Balance) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::Burn { -// id: id.into(), -// who: who.into(), -// amount: Compact(amount), -// })) -// } - -/// Move some assets from the sender account to another, keeping the sender account alive. -#[inline] -pub fn transfer_keep_alive(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, TRANSFER])) - .input::<(AssetId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, target, amount)) -} - -// /// Set the metadata for an asset. -// pub(crate) fn set_metadata( -// id: AssetId, -// name: Vec, -// symbol: Vec, -// decimals: u8, -// ) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::SetMetadata { id: id.into(), name, symbol, decimals })) -// } - -// /// Clear the metadata for an asset. -// pub(crate) fn clear_metadata(id: AssetId) -> Result<()> { -// dispatch(RuntimeCall::Assets(AssetsCall::ClearMetadata { id: id.into() })) -// } - -/// Approve an amount of asset for transfer by a delegated third-party account. -#[inline] -pub fn approve_transfer(id: AssetId, delegate: AccountId, amount: Balance) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, APPROVE])) - .input::<(AssetId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, delegate, amount)) -} - -/// Cancel all of some asset approved for delegated transfer by a third-party account. -#[inline] -pub fn cancel_approval(id: AssetId, delegate: AccountId) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, ASSETS, CANCEL_APPROVAL])) - .input::<(AssetId, AccountId)>() - .output::, true>() - .handle_error_code::() - .call(&(id, delegate)) -} - -/// Transfer some asset balance from a previously delegated account to some third-party -/// account. -#[inline] -pub fn transfer_approved( - id: AssetId, - from: AccountId, - to: AccountId, - amount: Balance, -) -> Result<()> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, ASSETS, TRANSFER_APPROVED])) - .input::<(AssetId, AccountId, AccountId, Balance)>() - .output::, true>() - .handle_error_code::() - .call(&(id, from, to, amount)) -} - -/// 2. Read state functions: -/// - total_supply -const TOTAL_SUPPLY: u8 = 0; -/// - balance_of -const BALANCE_OF: u8 = 1; -/// - allowance -const ALLOWANCE: u8 = 2; -/// - token_name -const TOKEN_NAME: u8 = 3; -/// - token_symbol -const TOKEN_SYMBOL: u8 = 4; -/// - token_decimals -const TOKEN_DECIMALS: u8 = 5; -/// - asset_exists - -#[inline] -pub fn total_supply(id: AssetId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOTAL_SUPPLY])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn balance_of(id: AssetId, owner: AccountId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, BALANCE_OF])) - .input::<(AssetId, AccountId)>() - .output::>, true>() - .handle_error_code::() - .call(&(id, owner)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, ALLOWANCE])) - .input::<(AssetId, AccountId, AccountId)>() - .output::>, true>() - .handle_error_code::() - .call(&(id, owner, spender)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn token_name(id: AssetId) -> Result> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOKEN_NAME])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} -// -#[inline] -pub fn token_symbol(id: AssetId) -> Result> { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOKEN_SYMBOL])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -#[inline] -pub fn token_decimals(id: AssetId) -> Result { - ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, ASSETS, TOKEN_DECIMALS])) - .input::() - .output::>, true>() - .handle_error_code::() - .call(&(id)) - .and_then(|v| ::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) -} - -// pub(crate) fn asset_exists(id: AssetId) -> Result { -// state::read(RuntimeStateKeys::Assets(AssetsKeys::AssetExists(id))) -// } diff --git a/pop-api/src/v0/balances.rs b/pop-api/src/v0/balances.rs deleted file mode 100644 index bf029178..00000000 --- a/pop-api/src/v0/balances.rs +++ /dev/null @@ -1,88 +0,0 @@ -use crate::{ - dispatch, primitives::MultiAddress, v0::RuntimeCall, AccountId, PopApiError, - PopApiError::UnknownStatusCode, -}; - -type Result = core::result::Result; - -pub fn transfer_keep_alive( - dest: impl Into>, - value: u128, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Balances(BalancesCall::TransferKeepAlive { - dest: dest.into(), - value, - }))?) -} - -#[derive(scale::Encode)] -#[allow(dead_code)] -pub(crate) enum BalancesCall { - #[codec(index = 3)] - TransferKeepAlive { - dest: MultiAddress, - #[codec(compact)] - value: u128, - }, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// Vesting balance too high to send value. - VestingBalance, - /// Account liquidity restrictions prevent withdrawal. - LiquidityRestrictions, - /// Balance too low to send value. - InsufficientBalance, - /// Value too low to create account due to existential deposit. - ExistentialDeposit, - /// Transfer/payment would kill account. - Expendability, - /// A vesting schedule already exists for this account. - ExistingVestingSchedule, - /// Beneficiary account must pre-exist. - DeadAccount, - /// Number of named reserves exceed `MaxReserves`. - TooManyReserves, - /// Number of holds exceed `VariantCountOf`. - TooManyHolds, - /// Number of freezes exceed `MaxFreezes`. - TooManyFreezes, - /// The issuance cannot be modified since it is already deactivated. - IssuanceDeactivated, - /// The delta cannot be zero. - DeltaZero, -} - -impl TryFrom for Error { - type Error = PopApiError; - - fn try_from(status_code: u32) -> core::result::Result { - use Error::*; - match status_code { - 0 => Ok(VestingBalance), - 1 => Ok(LiquidityRestrictions), - 2 => Ok(InsufficientBalance), - 3 => Ok(ExistentialDeposit), - 4 => Ok(Expendability), - 5 => Ok(ExistingVestingSchedule), - 6 => Ok(DeadAccount), - 7 => Ok(TooManyReserves), - 8 => Ok(TooManyHolds), - 9 => Ok(TooManyFreezes), - 10 => Ok(IssuanceDeactivated), - 11 => Ok(DeltaZero), - _ => Err(UnknownStatusCode(status_code)), - } - } -} - -impl From for Error { - fn from(error: PopApiError) -> Self { - match error { - PopApiError::Balances(e) => e, - _ => panic!("expected balances error"), - } - } -} diff --git a/pop-api/src/v0/cross_chain/coretime.rs b/pop-api/src/v0/cross_chain/coretime.rs deleted file mode 100644 index 0039ed20..00000000 --- a/pop-api/src/v0/cross_chain/coretime.rs +++ /dev/null @@ -1,11 +0,0 @@ -use crate::{ - primitives::cross_chain::{CrossChainMessage, OnDemand, RelayChainMessage}, - send_xcm, -}; - -/// Send a cross-chain message to place a sport order for instantaneous coretime. -pub fn place_spot_order(max_amount: u128, para_id: u32) -> crate::cross_chain::Result<()> { - Ok(send_xcm(CrossChainMessage::Relay(RelayChainMessage::OnDemand( - OnDemand::PlaceOrderKeepAlive { max_amount, para_id }, - )))?) -} diff --git a/pop-api/src/v0/cross_chain/mod.rs b/pop-api/src/v0/cross_chain/mod.rs deleted file mode 100644 index 6732c119..00000000 --- a/pop-api/src/v0/cross_chain/mod.rs +++ /dev/null @@ -1,107 +0,0 @@ -pub mod coretime; - -use crate::{PopApiError::UnknownStatusCode, *}; - -type Result = core::result::Result; - -#[derive(Debug, Copy, Clone, PartialEq, Eq, scale::Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// The desired destination was unreachable, generally because there is a no way of routing - /// to it. - Unreachable, - /// There was some other issue (i.e. not to do with routing) in sending the message. - /// Perhaps a lack of space for buffering the message. - SendFailure, - /// The message execution fails the filter. - Filtered, - /// The message's weight could not be determined. - UnweighableMessage, - /// The destination `Location` provided cannot be inverted. - DestinationNotInvertible, - /// The assets to be sent are empty. - Empty, - /// Could not re-anchor the assets to declare the fees for the destination chain. - CannotReanchor, - /// Too many assets have been attempted for transfer. - TooManyAssets, - /// Origin is invalid for sending. - InvalidOrigin, - /// The version of the `Versioned` value used is not able to be interpreted. - BadVersion, - /// The given location could not be used (e.g. because it cannot be expressed in the - /// desired version of XCM). - BadLocation, - /// The referenced subscription could not be found. - NoSubscription, - /// The location is invalid since it already has a subscription from us. - AlreadySubscribed, - /// Could not check-out the assets for teleportation to the destination chain. - CannotCheckOutTeleport, - /// The owner does not own (all) of the asset that they wish to do the operation on. - LowBalance, - /// The asset owner has too many locks on the asset. - TooManyLocks, - /// The given account is not an identifiable sovereign account for any location. - AccountNotSovereign, - /// The operation required fees to be paid which the initiator could not meet. - FeesNotMet, - /// A remote lock with the corresponding data could not be found. - LockNotFound, - /// The unlock operation cannot succeed because there are still consumers of the lock. - InUse, - /// Invalid non-concrete asset. - InvalidAssetNotConcrete, - /// Invalid asset, reserve chain could not be determined for it. - InvalidAssetUnknownReserve, - /// Invalid asset, do not support remote asset reserves with different fees reserves. - InvalidAssetUnsupportedReserve, - /// Too many assets with different reserve locations have been attempted for transfer. - TooManyReserves, - /// Local XCM execution incomplete. - LocalExecutionIncomplete, -} - -impl TryFrom for Error { - type Error = PopApiError; - - fn try_from(status_code: u32) -> core::result::Result { - use Error::*; - match status_code { - 0 => Ok(Unreachable), - 1 => Ok(SendFailure), - 2 => Ok(Filtered), - 3 => Ok(UnweighableMessage), - 4 => Ok(DestinationNotInvertible), - 5 => Ok(Empty), - 6 => Ok(CannotReanchor), - 7 => Ok(TooManyAssets), - 8 => Ok(InvalidOrigin), - 9 => Ok(BadVersion), - 10 => Ok(BadLocation), - 11 => Ok(NoSubscription), - 12 => Ok(AlreadySubscribed), - 13 => Ok(CannotCheckOutTeleport), - 14 => Ok(LowBalance), - 15 => Ok(TooManyLocks), - 16 => Ok(AccountNotSovereign), - 17 => Ok(FeesNotMet), - 18 => Ok(LockNotFound), - 19 => Ok(InUse), - 20 => Ok(InvalidAssetNotConcrete), - 21 => Ok(InvalidAssetUnknownReserve), - 22 => Ok(InvalidAssetUnsupportedReserve), - 23 => Ok(TooManyReserves), - _ => Err(UnknownStatusCode(status_code)), - } - } -} - -impl From for Error { - fn from(error: PopApiError) -> Self { - match error { - PopApiError::Xcm(e) => e, - _ => panic!("expected xcm error"), - } - } -} diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index f7dab6b4..4a0da363 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -1,13 +1,6 @@ use crate::{primitives::error::Error, StatusCode}; -#[cfg(feature = "assets")] pub mod assets; -#[cfg(feature = "balances")] -pub mod balances; -#[cfg(feature = "cross-chain")] -pub mod cross_chain; -#[cfg(feature = "nfts")] -pub mod nfts; pub(crate) const V0: u8 = 0; diff --git a/pop-api/src/v0/nfts.rs b/pop-api/src/v0/nfts.rs deleted file mode 100644 index 63b90a1f..00000000 --- a/pop-api/src/v0/nfts.rs +++ /dev/null @@ -1,883 +0,0 @@ -use super::RuntimeCall; -use crate::{PopApiError::UnknownStatusCode, *}; -use ink::prelude::vec::Vec; -use primitives::{ApprovalsLimit, BoundedBTreeMap, KeyLimit, MultiAddress}; -pub use primitives::{CollectionId, ItemId}; -use scale::Encode; -pub use types::*; - -type StringLimit = u32; -type MaxTips = u32; - -/// Issue a new collection of non-fungible items -pub fn create( - admin: impl Into>, - config: CollectionConfig, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Create { admin: admin.into(), config }))?) -} - -/// Destroy a collection of fungible items. -pub fn destroy(collection: CollectionId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Destroy { collection }))?) -} - -/// Mint an item of a particular collection. -pub fn mint( - collection: CollectionId, - item: ItemId, - mint_to: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Mint { - collection, - item, - mint_to: mint_to.into(), - witness_data: None, - }))?) -} - -/// Destroy a single item. -pub fn burn(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Burn { collection, item }))?) -} - -/// Move an item from the sender account to another. -pub fn transfer( - collection: CollectionId, - item: ItemId, - dest: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Transfer { collection, item, dest: dest.into() }))?) -} - -/// Re-evaluate the deposits on some items. -pub fn redeposit(collection: CollectionId, items: Vec) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::Redeposit { collection, items }))?) -} - -/// Change the Owner of a collection. -pub fn transfer_ownership( - collection: CollectionId, - new_owner: impl Into>, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::TransferOwnership { - collection, - new_owner: new_owner.into(), - }))?) -} - -/// Set (or reset) the acceptance of ownership for a particular account. -pub fn set_accept_ownership( - collection: CollectionId, - maybe_collection: Option, -) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAcceptOwnership { collection, maybe_collection }))?) -} - -/// Set the maximum number of items a collection could have. -pub fn set_collection_max_supply(collection: CollectionId, max_supply: u32) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMaxSupply { collection, max_supply }))?) -} - -/// Update mint settings. -pub fn update_mint_settings(collection: CollectionId, mint_settings: MintSettings) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::UpdateMintSettings { collection, mint_settings }))?) -} - -/// Get the owner of the item, if the item exists. -pub fn owner(collection: CollectionId, item: ItemId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Owner(collection, item)))?) -} - -/// Get the owner of the collection, if the collection exists. -pub fn collection_owner(collection: CollectionId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionOwner(collection)))?) -} - -/// Get the details of a collection. -pub fn collection(collection: CollectionId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Collection(collection)))?) -} - -/// Get the details of an item. -pub fn item(collection: CollectionId, item: ItemId) -> Result> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Item(collection, item)))?) -} - -pub mod approvals { - use super::*; - - /// Approve an item to be transferred by a delegated third-party account. - pub fn approve_transfer( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - maybe_deadline: Option, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveTransfer { - collection, - item, - delegate: delegate.into(), - maybe_deadline, - }))?) - } - - /// Cancel one of the transfer approvals for a specific item. - pub fn cancel_approval( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelApproval { - collection, - item, - delegate: delegate.into(), - }))?) - } - - /// Cancel all the approvals of a specific item. - pub fn clear_all_transfer_approvals(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAllTransferApprovals { collection, item }))?) - } -} - -pub mod attributes { - use super::*; - - /// Approve item's attributes to be changed by a delegated third-party account. - pub fn approve_item_attribute( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ApproveItemAttributes { - collection, - item, - delegate: delegate.into(), - }))?) - } - - /// Cancel the previously provided approval to change item's attributes. - pub fn cancel_item_attributes_approval( - collection: CollectionId, - item: ItemId, - delegate: impl Into>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelItemAttributesApproval { - collection, - item, - delegate: delegate.into(), - }))?) - } - - /// Set an attribute for a collection or item. - pub fn set_attribute( - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - value: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetAttribute { - collection, - maybe_item, - namespace, - key, - value, - }))?) - } - - /// Clear an attribute for a collection or item. - pub fn clear_attribute( - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearAttribute { - collection, - maybe_item, - namespace, - key, - }))?) - } - - /// Get the attribute value of `item` of `collection` corresponding to `key`. - pub fn attribute( - collection: CollectionId, - item: ItemId, - key: BoundedVec, - ) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::Attribute(collection, item, key)))?) - } - - // /// Get the custom attribute value of `item` of `collection` corresponding to `key`. - // pub fn custom_attribute( - // account: AccountId, - // collection: CollectionId, - // item: ItemId, - // key: BoundedVec, - // ) -> Result>> { - // Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CustomAttribute( - // account, collection, item, key, - // )))?) - // } - - /// Get the system attribute value of `item` of `collection` corresponding to `key` if - /// `item` is `Some`. Otherwise, returns the system attribute value of `collection` - /// corresponding to `key`. - pub fn system_attribute( - collection: CollectionId, - item: Option, - key: BoundedVec, - ) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::SystemAttribute(collection, item, key)))?) - } - - /// Get the attribute value of `item` of `collection` corresponding to `key`. - pub fn collection_attribute( - collection: CollectionId, - key: BoundedVec, - ) -> Result>> { - Ok(state::read(RuntimeStateKeys::Nfts(NftsKeys::CollectionAttribute(collection, key)))?) - } -} - -pub mod locking { - use super::*; - - /// Disallows changing the metadata or attributes of the item. - pub fn lock_item_properties( - collection: CollectionId, - item: ItemId, - lock_metadata: bool, - lock_attributes: bool, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemProperties { - collection, - item, - lock_metadata, - lock_attributes, - }))?) - } - - /// Disallow further unprivileged transfer of an item. - pub fn lock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockItemTransfer { collection, item }))?) - } - - /// Re-allow unprivileged transfer of an item. - pub fn unlock_item_transfer(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::UnlockItemTransfer { collection, item }))?) - } - - /// Disallows specified settings for the whole collection. - pub fn lock_collection( - collection: CollectionId, - lock_settings: CollectionSettings, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::LockCollection { collection, lock_settings }))?) - } -} - -pub mod metadata { - use super::*; - - /// Set the metadata for an item. - pub fn set_metadata( - collection: CollectionId, - item: ItemId, - data: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetMetadata { collection, item, data }))?) - } - - /// Clear the metadata for an item. - pub fn clear_metadata(collection: CollectionId, item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearMetadata { collection, item }))?) - } - - /// Set the metadata for a collection. - pub fn set_collection_metadata( - collection: CollectionId, - data: BoundedVec, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetCollectionMetadata { collection, data }))?) - } - - /// Clear the metadata for a collection. - pub fn clear_collection_metadata(collection: CollectionId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClearCollectionMetadata { collection }))?) - } -} - -pub mod roles { - use super::*; - - /// Change the Issuer, Admin and Freezer of a collection. - pub fn set_team( - collection: CollectionId, - issuer: Option>>, - admin: Option>>, - freezer: Option>>, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetTeam { - collection, - issuer: issuer.map(|i| i.into()), - admin: admin.map(|i| i.into()), - freezer: freezer.map(|i| i.into()), - }))?) - } -} - -pub mod trading { - use super::*; - - /// Allows to pay the tips. - pub fn pay_tips(tips: BoundedVec) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::PayTips { tips }))?) - } - - /// Set (or reset) the price for an item. - pub fn price(collection: CollectionId, item: ItemId, price: Option) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::SetPrice { collection, item, price }))?) - } - - /// Allows to buy an item if it's up for sale. - pub fn buy_item(collection: CollectionId, item: ItemId, bid_price: Balance) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::BuyItem { collection, item, bid_price }))?) - } - - pub mod swaps { - use super::*; - - /// Register a new atomic swap, declaring an intention to send an `item` in exchange for - /// `desired_item` from origin to target on the current chain. - pub fn create_swap( - offered_collection: CollectionId, - offered_item: ItemId, - desired_collection: CollectionId, - maybe_desired_item: Option, - maybe_price: Option, - duration: BlockNumber, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CreateSwap { - offered_collection, - offered_item, - desired_collection, - maybe_desired_item, - maybe_price, - duration, - }))?) - } - - /// Cancel an atomic swap. - pub fn cancel_swap(offered_collection: CollectionId, offered_item: ItemId) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::CancelSwap { - offered_collection, - offered_item, - }))?) - } - - /// Claim an atomic swap. - pub fn claim_swap( - send_collection: CollectionId, - send_item: ItemId, - receive_collection: CollectionId, - receive_item: ItemId, - ) -> Result<()> { - Ok(dispatch(RuntimeCall::Nfts(NftCalls::ClaimSwap { - send_collection, - send_item, - receive_collection, - receive_item, - }))?) - } - } -} - -#[derive(Encode)] -pub(crate) enum NftCalls { - #[codec(index = 0)] - Create { admin: MultiAddress, config: CollectionConfig }, - #[codec(index = 2)] - Destroy { collection: CollectionId }, - #[codec(index = 3)] - Mint { - collection: CollectionId, - item: ItemId, - mint_to: MultiAddress, - witness_data: Option<()>, - }, - #[codec(index = 5)] - Burn { collection: CollectionId, item: ItemId }, - #[codec(index = 6)] - Transfer { collection: CollectionId, item: ItemId, dest: MultiAddress }, - #[codec(index = 7)] - Redeposit { collection: CollectionId, items: Vec }, - #[codec(index = 8)] - LockItemTransfer { collection: CollectionId, item: ItemId }, - #[codec(index = 9)] - UnlockItemTransfer { collection: CollectionId, item: ItemId }, - #[codec(index = 10)] - LockCollection { collection: CollectionId, lock_settings: CollectionSettings }, - #[codec(index = 11)] - TransferOwnership { collection: CollectionId, new_owner: MultiAddress }, - #[codec(index = 12)] - SetTeam { - collection: CollectionId, - issuer: Option>, - admin: Option>, - freezer: Option>, - }, - #[codec(index = 15)] - ApproveTransfer { - collection: CollectionId, - item: ItemId, - delegate: MultiAddress, - maybe_deadline: Option, - }, - #[codec(index = 16)] - CancelApproval { collection: CollectionId, item: ItemId, delegate: MultiAddress }, - #[codec(index = 17)] - ClearAllTransferApprovals { collection: CollectionId, item: ItemId }, - #[codec(index = 18)] - LockItemProperties { - collection: CollectionId, - item: ItemId, - lock_metadata: bool, - lock_attributes: bool, - }, - #[codec(index = 19)] - SetAttribute { - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - value: BoundedVec, - }, - #[codec(index = 21)] - ClearAttribute { - collection: CollectionId, - maybe_item: Option, - namespace: AttributeNamespace, - key: BoundedVec, - }, - #[codec(index = 22)] - ApproveItemAttributes { - collection: CollectionId, - item: ItemId, - delegate: MultiAddress, - }, - #[codec(index = 23)] - CancelItemAttributesApproval { - collection: CollectionId, - item: ItemId, - delegate: MultiAddress, - }, - #[codec(index = 24)] - SetMetadata { collection: CollectionId, item: ItemId, data: BoundedVec }, - #[codec(index = 25)] - ClearMetadata { collection: CollectionId, item: ItemId }, - #[codec(index = 26)] - SetCollectionMetadata { collection: CollectionId, data: BoundedVec }, - #[codec(index = 27)] - ClearCollectionMetadata { collection: CollectionId }, - #[codec(index = 28)] - SetAcceptOwnership { collection: CollectionId, maybe_collection: Option }, - #[codec(index = 29)] - SetCollectionMaxSupply { collection: CollectionId, max_supply: u32 }, - #[codec(index = 30)] - UpdateMintSettings { collection: CollectionId, mint_settings: MintSettings }, - #[codec(index = 31)] - SetPrice { collection: CollectionId, item: ItemId, price: Option }, - #[codec(index = 32)] - BuyItem { collection: CollectionId, item: ItemId, bid_price: Balance }, - #[codec(index = 33)] - PayTips { tips: BoundedVec }, - #[codec(index = 34)] - CreateSwap { - offered_collection: CollectionId, - offered_item: ItemId, - desired_collection: CollectionId, - maybe_desired_item: Option, - maybe_price: Option, - duration: BlockNumber, - }, - #[codec(index = 35)] - CancelSwap { offered_collection: CollectionId, offered_item: ItemId }, - #[codec(index = 36)] - ClaimSwap { - send_collection: CollectionId, - send_item: ItemId, - receive_collection: CollectionId, - receive_item: ItemId, - }, -} - -#[derive(Debug, Copy, Clone, PartialEq, Eq, Encode, scale::Decode)] -#[cfg_attr(feature = "std", derive(scale_info::TypeInfo))] -pub enum Error { - /// The signing account has no permission to do the operation. - NoPermission, - /// The given item ID is unknown. - UnknownCollection, - /// The item ID has already been used for an item. - AlreadyExists, - /// The approval had a deadline that expired, so the approval isn't valid anymore. - ApprovalExpired, - /// The owner turned out to be different to what was expected. - WrongOwner, - /// The witness data given does not match the current state of the chain. - BadWitness, - /// Collection ID is already taken. - CollectionIdInUse, - /// Items within that collection are non-transferable. - ItemsNonTransferable, - /// The provided account is not a delegate. - NotDelegate, - /// The delegate turned out to be different to what was expected. - WrongDelegate, - /// No approval exists that would allow the transfer. - Unapproved, - /// The named owner has not signed ownership acceptance of the collection. - Unaccepted, - /// The item is locked (non-transferable). - ItemLocked, - /// Item's attributes are locked. - LockedItemAttributes, - /// Collection's attributes are locked. - LockedCollectionAttributes, - /// Item's metadata is locked. - LockedItemMetadata, - /// Collection's metadata is locked. - LockedCollectionMetadata, - /// All items have been minted. - MaxSupplyReached, - /// The max supply is locked and can't be changed. - MaxSupplyLocked, - /// The provided max supply is less than the number of items a collection already has. - MaxSupplyTooSmall, - /// The given item ID is unknown. - UnknownItem, - /// Swap doesn't exist. - UnknownSwap, - /// The given item has no metadata set. - MetadataNotFound, - /// The provided attribute can't be found. - AttributeNotFound, - /// Item is not for sale. - NotForSale, - /// The provided bid is too low. - BidTooLow, - /// The item has reached its approval limit. - ReachedApprovalLimit, - /// The deadline has already expired. - DeadlineExpired, - /// The duration provided should be less than or equal to `MaxDeadlineDuration`. - WrongDuration, - /// The method is disabled by system settings. - MethodDisabled, - /// The provided setting can't be set. - WrongSetting, - /// Item's config already exists and should be equal to the provided one. - InconsistentItemConfig, - /// Config for a collection or an item can't be found. - NoConfig, - /// Some roles were not cleared. - RolesNotCleared, - /// Mint has not started yet. - MintNotStarted, - /// Mint has already ended. - MintEnded, - /// The provided Item was already used for claiming. - AlreadyClaimed, - /// The provided data is incorrect. - IncorrectData, - /// The extrinsic was sent by the wrong origin. - WrongOrigin, - /// The provided signature is incorrect. - WrongSignature, - /// The provided metadata might be too long. - IncorrectMetadata, - /// Can't set more attributes per one call. - MaxAttributesLimitReached, - /// The provided namespace isn't supported in this call. - WrongNamespace, - /// Can't delete non-empty collections. - CollectionNotEmpty, - /// The witness data should be provided. - WitnessRequired, -} - -impl TryFrom for Error { - type Error = PopApiError; - - fn try_from(status_code: u32) -> core::result::Result { - use Error::*; - match status_code { - 0 => Ok(NoPermission), - 1 => Ok(UnknownCollection), - 2 => Ok(AlreadyExists), - 3 => Ok(ApprovalExpired), - 4 => Ok(WrongOwner), - 5 => Ok(BadWitness), - 6 => Ok(CollectionIdInUse), - 7 => Ok(ItemsNonTransferable), - 8 => Ok(NotDelegate), - 9 => Ok(WrongDelegate), - 10 => Ok(Unapproved), - 11 => Ok(Unaccepted), - 12 => Ok(ItemLocked), - 13 => Ok(LockedItemAttributes), - 14 => Ok(LockedCollectionAttributes), - 15 => Ok(LockedItemMetadata), - 16 => Ok(LockedCollectionMetadata), - 17 => Ok(MaxSupplyReached), - 18 => Ok(MaxSupplyLocked), - 19 => Ok(MaxSupplyTooSmall), - 20 => Ok(UnknownItem), - 21 => Ok(UnknownSwap), - 22 => Ok(MetadataNotFound), - 23 => Ok(AttributeNotFound), - 24 => Ok(NotForSale), - 25 => Ok(BidTooLow), - 26 => Ok(ReachedApprovalLimit), - 27 => Ok(DeadlineExpired), - 28 => Ok(WrongDuration), - 29 => Ok(MethodDisabled), - 30 => Ok(WrongSetting), - 31 => Ok(InconsistentItemConfig), - 32 => Ok(NoConfig), - 33 => Ok(RolesNotCleared), - 34 => Ok(MintNotStarted), - 35 => Ok(MintEnded), - 36 => Ok(AlreadyClaimed), - 37 => Ok(IncorrectData), - 38 => Ok(WrongOrigin), - 39 => Ok(WrongSignature), - 40 => Ok(IncorrectMetadata), - 41 => Ok(MaxAttributesLimitReached), - 42 => Ok(WrongNamespace), - 43 => Ok(CollectionNotEmpty), - 44 => Ok(WitnessRequired), - _ => Err(UnknownStatusCode(status_code)), - } - } -} - -impl From for Error { - fn from(error: PopApiError) -> Self { - match error { - PopApiError::Nfts(e) => e, - _ => panic!("expected nfts error"), - } - } -} - -// Local implementations of pallet-nfts types -mod types { - use super::*; - use crate::{ - primitives::{CollectionId, ItemId}, - Balance, BlockNumber, - }; - pub use enumflags2::{bitflags, BitFlags}; - use scale::{Decode, EncodeLike, MaxEncodedLen}; - use scale_info::{build::Fields, meta_type, prelude::vec, Path, Type, TypeInfo, TypeParameter}; - - /// Attribute namespaces for non-fungible tokens. - #[derive(Encode)] - pub enum AttributeNamespace { - /// An attribute was set by the pallet. - Pallet, - /// An attribute was set by collection's owner. - CollectionOwner, - /// An attribute was set by item's owner. - ItemOwner, - /// An attribute was set by pre-approved account. - Account(AccountId), - } - - /// Collection's configuration. - #[derive(Encode)] - pub struct CollectionConfig { - /// Collection's settings. - pub settings: CollectionSettings, - /// Collection's max supply. - pub max_supply: Option, - /// Default settings each item will get during the mint. - pub mint_settings: MintSettings, - } - - /// Information about a collection. - #[derive(Decode, Debug, Encode, Eq, PartialEq)] - pub struct CollectionDetails { - /// Collection's owner. - pub owner: AccountId, - /// The total balance deposited by the owner for all the storage data associated with this - /// collection. Used by `destroy`. - pub owner_deposit: Balance, - /// The total number of outstanding items of this collection. - pub items: u32, - /// The total number of outstanding item metadata of this collection. - pub item_metadatas: u32, - /// The total number of outstanding item configs of this collection. - pub item_configs: u32, - /// The total number of attributes for this collection. - pub attributes: u32, - } - - /// Wrapper type for `BitFlags` that implements `Codec`. - pub struct CollectionSettings(pub BitFlags); - - impl_codec_bitflags!(CollectionSettings, u64, CollectionSetting); - - /// Support for up to 64 user-enabled features on a collection. - #[bitflags] - #[repr(u64)] - #[derive(Copy, Clone, Encode, TypeInfo)] - pub enum CollectionSetting { - /// Items in this collection are transferable. - TransferableItems, - /// The metadata of this collection can be modified. - UnlockedMetadata, - /// Attributes of this collection can be modified. - UnlockedAttributes, - /// The supply of this collection can be modified. - UnlockedMaxSupply, - /// When this isn't set then the deposit is required to hold the items of this collection. - DepositRequired, - } - - /// Information concerning the ownership of a single unique item. - #[derive(Decode, Debug, Encode, Eq, PartialEq)] - pub struct ItemDetails { - /// The owner of this item. - pub owner: AccountId, - /// The approved transferrer of this item, if one is set. - pub approvals: BoundedBTreeMap, ApprovalsLimit>, - /// The amount held in the pallet's default account for this item. Free-hold items will - /// have this as zero. - pub deposit: Balance, - } - - /// Support for up to 64 user-enabled features on an item. - #[bitflags] - #[repr(u64)] - #[derive(Copy, Clone, Encode, TypeInfo)] - pub enum ItemSetting { - /// This item is transferable. - Transferable, - /// The metadata of this item can be modified. - UnlockedMetadata, - /// Attributes of this item can be modified. - UnlockedAttributes, - } - - /// Wrapper type for `BitFlags` that implements `Codec`. - pub struct ItemSettings(pub BitFlags); - - impl_codec_bitflags!(ItemSettings, u64, ItemSetting); - - /// Information about the tip. - #[derive(Encode)] - pub struct ItemTip { - /// The collection of the item. - pub(super) collection: CollectionId, - /// An item of which the tip is sent for. - pub(super) item: ItemId, - /// A sender of the tip. - pub(super) receiver: AccountId, - /// An amount the sender is willing to tip. - pub(super) amount: Balance, - } - - /// Holds the information about minting. - #[derive(Encode)] - pub struct MintSettings { - /// Whether anyone can mint or if minters are restricted to some subset. - pub mint_type: MintType, - /// An optional price per mint. - pub price: Option, - /// When the mint starts. - pub start_block: Option, - /// When the mint ends. - pub end_block: Option, - /// Default settings each item will get during the mint. - pub default_item_settings: ItemSettings, - } - - /// Mint type. Can the NFT be created by anyone, or only the creator of the collection, - /// or only by wallets that already hold an NFT from a certain collection? - /// The ownership of a privately minted NFT is still publicly visible. - #[derive(Encode)] - pub enum MintType { - /// Only an `Issuer` could mint items. - Issuer, - /// Anyone could mint items. - Public, - /// Only holders of items in specified collection could mint new items. - HolderOf(CollectionId), - } - - /// Holds the details about the price. - #[derive(Encode)] - pub struct PriceWithDirection { - /// An amount. - pub(super) amount: Balance, - /// A direction (send or receive). - pub(super) direction: PriceDirection, - } - - /// Specifies whether the tokens will be sent or received. - #[derive(Encode)] - pub enum PriceDirection { - /// Tokens will be sent. - Send, - /// Tokens will be received. - Receive, - } - - macro_rules! impl_codec_bitflags { - ($wrapper:ty, $size:ty, $bitflag_enum:ty) => { - impl MaxEncodedLen for $wrapper { - fn max_encoded_len() -> usize { - <$size>::max_encoded_len() - } - } - impl Encode for $wrapper { - fn using_encoded R>(&self, f: F) -> R { - self.0.bits().using_encoded(f) - } - } - impl EncodeLike for $wrapper {} - impl Decode for $wrapper { - fn decode( - input: &mut I, - ) -> core::result::Result { - let field = <$size>::decode(input)?; - Ok(Self(BitFlags::from_bits(field as $size).map_err(|_| "invalid value")?)) - } - } - - impl TypeInfo for $wrapper { - type Identity = Self; - - fn type_info() -> Type { - Type::builder() - .path(Path::new("BitFlags", module_path!())) - .type_params(vec![TypeParameter::new( - "T", - Some(meta_type::<$bitflag_enum>()), - )]) - .composite( - Fields::unnamed() - .field(|f| f.ty::<$size>().type_name(stringify!($bitflag_enum))), - ) - } - } - }; - } - pub(crate) use impl_codec_bitflags; -} diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index 5cbd6d6c..a12c9363 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pop-primitives" +name = "primitives" license = "GPL-3.0-only" version = "0.0.0" edition = "2021" @@ -16,6 +16,4 @@ std = [ "scale/std", "scale-info/std", ] -assets = [] -cross-chain = [] -nfts = [] +fungibles = [] \ No newline at end of file diff --git a/primitives/src/cross_chain.rs b/primitives/src/cross_chain.rs deleted file mode 100644 index 381e6a61..00000000 --- a/primitives/src/cross_chain.rs +++ /dev/null @@ -1,19 +0,0 @@ -use scale::{Decode, Encode, MaxEncodedLen}; - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum CrossChainMessage { - Relay(RelayChainMessage), -} - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum RelayChainMessage { - // Rococo index: https://github.com/paritytech/polkadot-sdk/blob/629506ce061db76d31d4f7a81f4a497752b27259/polkadot/runtime/rococo/src/lib.rs#L1423 - #[codec(index = 66)] - OnDemand(OnDemand), -} - -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum OnDemand { - #[codec(index = 1)] - PlaceOrderKeepAlive { max_amount: u128, para_id: u32 }, -} diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 376b440d..55e84b46 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -6,10 +6,6 @@ use scale::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; pub use v0::error; -#[cfg(feature = "cross-chain")] -pub mod cross_chain; -pub mod storage_keys; - /// An opaque 32-byte cryptographic identifier. #[derive(Encode, Decode, Debug, MaxEncodedLen, Eq, PartialEq)] #[cfg_attr(feature = "std", derive(TypeInfo))] @@ -17,19 +13,20 @@ pub struct AccountId(pub [u8; 32]); /// Identifier for the class of asset. pub type AssetId = u32; +pub const FUNGIBLES: u8 = 150; -#[cfg(feature = "nfts")] -pub mod nfts { - use bounded_collections::ConstU32; - - /// Id used for identifying non-fungible collections. - pub type CollectionId = u32; - /// Id used for identifying non-fungible items. - pub type ItemId = u32; - /// The maximum length of an attribute key. - pub type KeyLimit = ConstU32<64>; - /// The maximum approvals an item could have. - pub type ApprovalsLimit = ConstU32<20>; +#[cfg(feature = "fungibles")] +pub mod constants { + /// Module index. + pub const FUNGIBLES: u8 = 150; + pub mod fungibles { + pub const TOTAL_SUPPLY: u8 = 0; + pub const BALANCE_OF: u8 = 1; + pub const ALLOWANCE: u8 = 2; + pub const TOKEN_NAME: u8 = 3; + pub const TOKEN_SYMBOL: u8 = 4; + pub const TOKEN_DECIMALS: u8 = 5; + } } pub mod v0 { diff --git a/primitives/src/storage_keys.rs b/primitives/src/storage_keys.rs deleted file mode 100644 index 15be3953..00000000 --- a/primitives/src/storage_keys.rs +++ /dev/null @@ -1,30 +0,0 @@ -#[cfg(feature = "nfts")] -use super::nfts::*; -use super::*; - -#[cfg(feature = "cross-chain")] -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum ParachainSystemKeys { - /// Get the last relay chain block number seen by the parachain. - LastRelayChainBlockNumber, -} - -// https://github.com/paritytech/polkadot-sdk/blob/master/substrate/frame/nfts/src/impl_nonfungibles.rs -#[cfg(feature = "nfts")] -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -pub enum NftsKeys { - // Get the details of a collection. - Collection(CollectionId), - /// Get the owner of the collection, if the collection exists. - CollectionOwner(CollectionId), - // Get the details of an item. - Item(CollectionId, ItemId), - /// Get the owner of the item, if the item exists. - Owner(CollectionId, ItemId), - /// Get the attribute value of `item` of `collection` corresponding to `key`. - Attribute(CollectionId, ItemId, BoundedVec), - /// Get the system attribute value of `item` of `collection` corresponding to `key` - SystemAttribute(CollectionId, Option, BoundedVec), - /// Get the attribute value of `item` of `collection` corresponding to `key`. - CollectionAttribute(CollectionId, BoundedVec), -} diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 875640e2..80a0fe48 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -22,7 +22,7 @@ scale-info.workspace = true smallvec.workspace = true # Local -pop-primitives = { workspace = true, features = ["assets", "cross-chain", "nfts"] } +primitives = { workspace = true, features = ["fungibles"] } pop-runtime-common.workspace = true pallet-api.workspace = true @@ -140,7 +140,7 @@ std = [ "parachains-common/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", - "pop-primitives/std", + "primitives/std", "scale-info/std", "sp-api/std", "sp-io/std", diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index a2076eae..785e76be 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -1,37 +1,29 @@ -use codec::{Compact, Decode, Encode}; -use frame_support::traits::{Contains, OriginTrait}; +use codec::{Decode, Encode}; use frame_support::{ dispatch::{GetDispatchInfo, RawOrigin}, pallet_prelude::*, - traits::fungibles::{ - approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, + traits::{ + fungibles::{approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect}, + Contains, OriginTrait, }, }; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, }; use sp_core::crypto::UncheckedFrom; -use sp_runtime::{traits::Dispatchable, DispatchError, MultiAddress}; -use sp_std::{boxed::Box, vec::Vec}; -use xcm::{ - latest::{prelude::*, OriginKind::SovereignAccount}, - VersionedXcm, -}; +use sp_runtime::{traits::Dispatchable, DispatchError}; +use sp_std::vec::Vec; use crate::{ config::assets::TrustBackedAssetsInstance, fungibles::{ self, - AssetsKeys::{self, *}, + Keys::{self, *}, }, state_keys::RuntimeStateKeys, AccountId, AllowedApiCalls, Balance, Runtime, RuntimeCall, RuntimeOrigin, UNIT, }; -use pop_primitives::{ - cross_chain::CrossChainMessage, - nfts::{CollectionId, ItemId}, - AssetId, -}; +use primitives::AssetId; mod v0; @@ -48,11 +40,8 @@ pub struct PopApiExtension; impl ChainExtension for PopApiExtension where T: pallet_contracts::Config - + pallet_xcm::Config + pallet_assets::Config + fungibles::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config + frame_system::Config< RuntimeOrigin = RuntimeOrigin, AccountId = AccountId, @@ -99,8 +88,6 @@ where FuncId::ReadState => { read_state::(&mut env, version, pallet_index, call_index, params) }, - // TODO - FuncId::SendXcm => send_xcm::(&mut env), } }, Err(e) => Err(e), @@ -183,8 +170,6 @@ where T: pallet_contracts::Config + pallet_assets::Config + fungibles::Config - + pallet_nfts::Config - + cumulus_pallet_parachain_system::Config + frame_system::Config, E: Ext, { @@ -200,7 +185,7 @@ where let result = match key { VersionedRuntimeStateKeys::V0(key) => match key { - RuntimeStateKeys::Assets(key) => read_assets_state::(key, env), + RuntimeStateKeys::Fungibles(key) => read_fungibles_state::(key, env), }, }? .encode(); @@ -225,50 +210,6 @@ enum VersionedRuntimeCall { V0(RuntimeCall), } -fn send_xcm(env: &mut Environment) -> Result<(), DispatchError> -where - T: pallet_contracts::Config - + frame_system::Config< - RuntimeOrigin = RuntimeOrigin, - AccountId = AccountId, - RuntimeCall = RuntimeCall, - >, - E: Ext, -{ - const LOG_PREFIX: &str = " send_xcm |"; - // Read the input as CrossChainMessage. - let xc_call: CrossChainMessage = env.read_as::()?; - // Determine the call to dispatch. - let (dest, message) = match xc_call { - CrossChainMessage::Relay(message) => { - let dest = Location::parent().into_versioned(); - let assets: Asset = (Here, 10 * UNIT).into(); - let beneficiary: Location = - AccountId32 { id: (env.ext().address().clone()).into(), network: None }.into(); - let message = Xcm::builder() - .withdraw_asset(assets.clone().into()) - .buy_execution(assets.clone(), Unlimited) - .transact( - SovereignAccount, - Weight::from_parts(250_000_000, 10_000), - message.encode().into(), - ) - .refund_surplus() - .deposit_asset(assets.into(), beneficiary) - .build(); - (dest, message) - }, - }; - // TODO: revisit to replace with signed contract origin - let origin: RuntimeOrigin = RawOrigin::Root.into(); - // Generate runtime call to dispatch. - let call = RuntimeCall::PolkadotXcm(pallet_xcm::Call::send { - dest: Box::new(dest), - message: Box::new(VersionedXcm::V4(message)), - }); - dispatch_call::(env, call, origin, LOG_PREFIX) -} - // Converts a `DispatchError` to a `u32` status code based on the version of the API the contract uses. // The contract calling the chain extension can convert the status code to the descriptive `Error`. // @@ -328,7 +269,6 @@ pub(crate) fn convert_to_status_code(error: DispatchError, version: u8) -> u32 { pub enum FuncId { Dispatch, ReadState, - SendXcm, } impl TryFrom for FuncId { @@ -342,7 +282,6 @@ impl TryFrom for FuncId { let id = match func_id { 0 => Self::Dispatch, 1 => Self::ReadState, - 2 => Self::SendXcm, _ => { return Err(DispatchError::Other("UnknownFuncId")); }, @@ -351,8 +290,8 @@ impl TryFrom for FuncId { } } -fn read_assets_state( - key: AssetsKeys, +fn read_fungibles_state( + key: Keys, env: &mut Environment, ) -> Result, DispatchError> where @@ -366,37 +305,12 @@ where match key { TotalSupply(id) => Ok(fungibles::Pallet::::total_supply(id).encode()), BalanceOf(id, owner) => Ok(fungibles::Pallet::::balance_of(id, &owner).encode()), - _ => todo!(), - // Allowance(id, owner, spender) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_assets::Pallet::::allowance( - // id, - // &owner.0.into(), - // &spender.0.into(), - // ) - // .encode()) - // }, - // TokenName(id) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok( as MetadataInspect< - // AccountId, - // >>::name(id) - // .encode()) - // }, - // TokenSymbol(id) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok( as MetadataInspect< - // AccountId, - // >>::symbol(id) - // .encode()) - // }, - // TokenDecimals(id) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok( as MetadataInspect< - // AccountId, - // >>::decimals(id) - // .encode()) - // }, + Allowance(id, owner, spender) => { + Ok(fungibles::Pallet::::allowance(id, &owner, &spender).encode()) + }, + TokenName(id) => Ok(fungibles::Pallet::::token_name(id).encode()), + TokenSymbol(id) => Ok(fungibles::Pallet::::token_symbol(id).encode()), + TokenDecimals(id) => Ok(fungibles::Pallet::::token_decimals(id).encode()), // AssetsKeys::AssetExists(id) => { // env.charge_weight(T::DbWeight::get().reads(1_u64))?; // Ok(pallet_assets::Pallet::::asset_exists(id).encode()) @@ -409,6 +323,7 @@ mod tests { use super::*; use crate::{Assets, Runtime, System}; use sp_runtime::BuildStorage; + // Test ensuring `func_id()` and `ext_id()` work as expected, i.e. extracting the first two // bytes and the last two bytes, respectively, from a 4 byte array. #[test] diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index f6042d55..d198025a 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -256,41 +256,10 @@ impl Contains for FilteredCalls { pub struct AllowedApiCalls; impl Contains for AllowedApiCalls { fn contains(c: &RuntimeCall) -> bool { - use config::assets::AssetsCall; - use pallet_nfts::Call as NftsCall; + use fungibles::Call as FungiblesCall; matches!( c, - RuntimeCall::Balances(BalancesCall::transfer_keep_alive { .. }) - | RuntimeCall::Assets( - AssetsCall::create { .. } - | AssetsCall::start_destroy { .. } - | AssetsCall::destroy_accounts { .. } - | AssetsCall::destroy_approvals { .. } - | AssetsCall::finish_destroy { .. } - | AssetsCall::mint { .. } - | AssetsCall::burn { .. } - | AssetsCall::transfer { .. } - | AssetsCall::transfer_keep_alive { .. } - | AssetsCall::force_transfer { .. } - | AssetsCall::freeze { .. } - | AssetsCall::thaw { .. } - | AssetsCall::freeze_asset { .. } - | AssetsCall::thaw_asset { .. } - | AssetsCall::transfer_ownership { .. } - | AssetsCall::set_team { .. } - | AssetsCall::set_metadata { .. } - | AssetsCall::clear_metadata { .. } - | AssetsCall::approve_transfer { .. } - | AssetsCall::cancel_approval { .. } - | AssetsCall::force_cancel_approval { .. } - | AssetsCall::transfer_approved { .. } - | AssetsCall::touch { .. } - | AssetsCall::refund { .. } - | AssetsCall::set_min_balance { .. } - | AssetsCall::touch_other { .. } - | AssetsCall::refund_other { .. } - | AssetsCall::block { .. } - ) | RuntimeCall::Fungibles(fungibles::Call::transfer { .. }) + RuntimeCall::Fungibles(FungiblesCall::transfer { .. } | FungiblesCall::approve { .. }) ) } } @@ -1004,19 +973,21 @@ cumulus_pallet_parachain_system::register_validate_block! { } pub(crate) mod state_keys { - use super::{fungibles, Runtime}; + use super::fungibles; use codec::{Decode, Encode, MaxEncodedLen}; + use primitives::constants::FUNGIBLES; #[derive(Encode, Decode, Debug, MaxEncodedLen)] + #[repr(u8)] pub enum RuntimeStateKeys { - #[codec(index = 52)] - Assets(fungibles::AssetsKeys), + Fungibles(fungibles::Keys) = FUNGIBLES, } } #[test] fn check_encoding() { use codec::{Compact, Encode}; + use primitives::constants::FUNGIBLES; use sp_runtime::{AccountId32, MultiAddress}; let id = Compact(5u32); @@ -1028,6 +999,6 @@ fn check_encoding() { value, }) .encode(); - let encoded = (150u8, 10u8, id, spender, value).encode(); + let encoded = (FUNGIBLES, 10u8, id, spender, value).encode(); assert_eq!(encoded_runtime_call, encoded); } diff --git a/runtime/testnet/Cargo.toml b/runtime/testnet/Cargo.toml index b04c3102..f269bed8 100644 --- a/runtime/testnet/Cargo.toml +++ b/runtime/testnet/Cargo.toml @@ -22,8 +22,8 @@ scale-info.workspace = true smallvec.workspace = true # Local -pop-primitives = { workspace = true, features = ["nfts", "cross-chain"] } -pop-runtime-common = { workspace = true, default-features = false } +primitives.workspace = true +pop-runtime-common.workspace = true # Substrate frame-benchmarking.workspace = true @@ -137,7 +137,7 @@ std = [ "parachains-common/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", - "pop-primitives/std", + "primitives/std", "scale-info/std", "sp-api/std", "sp-io/std", From 224390a9d4eb6d3ed458a1dc4d44e268b85d64d8 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 22 Jul 2024 12:02:17 +0200 Subject: [PATCH 05/56] refactor 2 --- pallets/api/src/fungibles/mod.rs | 45 ++++++++----- pop-api/src/lib.rs | 5 +- pop-api/src/primitives.rs | 1 - pop-api/src/v0/assets/fungibles.rs | 45 ++++++++----- primitives/src/lib.rs | 20 ------ .../rustc-ice-2024-07-20T20_16_19-33192.txt | 67 ------------------- runtime/devnet/src/extensions/mod.rs | 61 +++++++---------- .../src/extensions/{v0/error.rs => v0.rs} | 0 runtime/devnet/src/extensions/v0/mod.rs | 1 - runtime/devnet/src/lib.rs | 13 ++-- 10 files changed, 94 insertions(+), 164 deletions(-) delete mode 100644 runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt rename runtime/devnet/src/extensions/{v0/error.rs => v0.rs} (100%) delete mode 100644 runtime/devnet/src/extensions/v0/mod.rs diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 3f5f1742..32ec76c0 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -18,8 +18,6 @@ pub mod pallet { use pallet_assets::WeightInfo; use sp_runtime::traits::StaticLookup; - use primitives::constants::fungibles::*; - type AccountIdOf = ::AccountId; type AssetIdOf = > as Inspect< ::AccountId, @@ -37,13 +35,19 @@ pub mod pallet { #[derive(Encode, Decode, Debug, MaxEncodedLen)] #[repr(u8)] #[allow(clippy::unnecessary_cast)] - pub enum Keys { - TotalSupply(AssetIdOf) = TOTAL_SUPPLY, - BalanceOf(AssetIdOf, AccountIdOf) = BALANCE_OF, - Allowance(AssetIdOf, AccountIdOf, AccountIdOf) = ALLOWANCE, - TokenName(AssetIdOf) = TOKEN_NAME, - TokenSymbol(AssetIdOf) = TOKEN_SYMBOL, - TokenDecimals(AssetIdOf) = TOKEN_DECIMALS, + pub enum FungiblesKey { + #[codec(index = 0)] + TotalSupply(AssetIdOf), + #[codec(index = 1)] + BalanceOf(AssetIdOf, AccountIdOf), + #[codec(index = 2)] + Allowance(AssetIdOf, AccountIdOf, AccountIdOf), + #[codec(index = 3)] + TokenName(AssetIdOf), + #[codec(index = 4)] + TokenSymbol(AssetIdOf), + #[codec(index = 5)] + TokenDecimals(AssetIdOf), } #[pallet::config] @@ -56,7 +60,7 @@ pub mod pallet { #[pallet::call] impl Pallet { - #[pallet::call_index(9)] + #[pallet::call_index(0)] #[pallet::weight(AssetsWeightInfo::::transfer_keep_alive())] pub fn transfer( origin: OriginFor, @@ -68,7 +72,7 @@ pub mod pallet { Assets::::transfer_keep_alive(origin, id.into(), target, amount) } - #[pallet::call_index(10)] + #[pallet::call_index(2)] #[pallet::weight(AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] pub fn approve( origin: OriginFor, @@ -80,14 +84,21 @@ pub mod pallet { let id: AssetIdParameterOf = id.into(); Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; - Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { - e.with_weight( - AssetsWeightInfo::::cancel_approval() - + AssetsWeightInfo::::approve_transfer(), - ) - })?; + Assets::::approve_transfer(origin, id, spender, value)?; Ok(().into()) } + + #[pallet::call_index(3)] + #[pallet::weight(AssetsWeightInfo::::approve_transfer())] + pub fn increase_allowance( + origin: OriginFor, + id: AssetIdOf, + spender: AccountIdOf, + value: BalanceOf, + ) -> DispatchResult { + let spender = T::Lookup::unlookup(spender); + Assets::::approve_transfer(origin, id.into(), spender, value) + } } impl Pallet { diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index 7bb33555..a3f75f3f 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -1,9 +1,7 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -use ink::env::chain_extension::FromStatusCode; - use constants::DECODING_FAILED; - +use ink::env::chain_extension::FromStatusCode; pub use v0::assets; pub mod primitives; @@ -24,6 +22,7 @@ mod constants { // Modules: pub(crate) const ASSETS: u8 = 52; pub(crate) const BALANCES: u8 = 10; + pub(crate) const FUNGIBLES: u8 = 150; } /// Represents a status code returned by the runtime. diff --git a/pop-api/src/primitives.rs b/pop-api/src/primitives.rs index f0429b3f..34d7c38d 100644 --- a/pop-api/src/primitives.rs +++ b/pop-api/src/primitives.rs @@ -1,5 +1,4 @@ use ink::env::{DefaultEnvironment, Environment}; - pub use primitives::*; pub(crate) type AccountId = ::AccountId; diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 0a8ea8ab..0539ebb0 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -1,35 +1,45 @@ use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; use crate::{ - constants::{ASSETS, BALANCES, DECODING_FAILED, DISPATCH, READ_STATE}, + constants::{ASSETS, BALANCES, DECODING_FAILED, DISPATCH, FUNGIBLES, READ_STATE}, primitives::{AccountId, AssetId, Balance}, v0::V0, Result, StatusCode, }; use constants::*; pub use metadata::*; -use primitives::constants::{fungibles::*, FUNGIBLES}; /// Local Fungibles: /// 1. PSP-22 Interface /// 2. PSP-22 Metadata Interface /// 3. Asset Management -pub(super) mod constants { +mod constants { /// 1. PSP-22 Interface: /// - total_supply + pub const TOTAL_SUPPLY: u8 = 0; /// - balance_of + pub const BALANCE_OF: u8 = 1; /// - allowance + pub const ALLOWANCE: u8 = 2; /// - transfer - pub(super) const TRANSFER: u8 = 9; + pub(super) const TRANSFER: u8 = 0; /// - transfer_from - pub(super) const TRANSFER_FROM: u8 = 9; + pub(super) const TRANSFER_FROM: u8 = 1; /// - approve - pub(super) const APPROVE: u8 = 10; + pub(super) const APPROVE: u8 = 2; /// - increase_allowance - pub(super) const _INCREASE_ALLOWANCE: u8 = 10; + pub(super) const INCREASE_ALLOWANCE: u8 = 3; /// - decrease_allowance - pub(super) const _DECREASE_ALLOWANCE: u8 = 10; + pub(super) const DECREASE_ALLOWANCE: u8 = 4; + + /// 2. PSP-22 Metadata Interface: + /// - token_name + pub const TOKEN_NAME: u8 = 3; + /// - token_symbol + pub const TOKEN_SYMBOL: u8 = 4; + /// - token_decimals + pub const TOKEN_DECIMALS: u8 = 5; // 3. Asset Management: // - create @@ -227,8 +237,12 @@ pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] -pub fn increase_allowance(_id: AssetId, _spender: AccountId, _value: Balance) -> Result<()> { - Ok(()) +pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, INCREASE_ALLOWANCE])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, spender, value)) } /// Decreases the allowance of a spender. @@ -241,11 +255,12 @@ pub fn increase_allowance(_id: AssetId, _spender: AccountId, _value: Balance) -> /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. #[inline] -pub fn decrease_allowance(_id: AssetId, _spender: AccountId, _value: Balance) -> Result<()> { - // let allowance = assets::allowance(id, owner, spender)?; - // assets::cancel_approval(id, spender.clone())?; - // assets::approve_transfer(id, spender, value) - Ok(()) +pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { + ChainExtensionMethod::build(u32::from_le_bytes([V0, DISPATCH, FUNGIBLES, DECREASE_ALLOWANCE])) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, spender, value)) } pub mod metadata { diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index 55e84b46..e29f933c 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -6,28 +6,8 @@ use scale::{Decode, Encode, MaxEncodedLen}; use scale_info::TypeInfo; pub use v0::error; -/// An opaque 32-byte cryptographic identifier. -#[derive(Encode, Decode, Debug, MaxEncodedLen, Eq, PartialEq)] -#[cfg_attr(feature = "std", derive(TypeInfo))] -pub struct AccountId(pub [u8; 32]); - /// Identifier for the class of asset. pub type AssetId = u32; -pub const FUNGIBLES: u8 = 150; - -#[cfg(feature = "fungibles")] -pub mod constants { - /// Module index. - pub const FUNGIBLES: u8 = 150; - pub mod fungibles { - pub const TOTAL_SUPPLY: u8 = 0; - pub const BALANCE_OF: u8 = 1; - pub const ALLOWANCE: u8 = 2; - pub const TOKEN_NAME: u8 = 3; - pub const TOKEN_SYMBOL: u8 = 4; - pub const TOKEN_DECIMALS: u8 = 5; - } -} pub mod v0 { use super::*; diff --git a/runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt b/runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt deleted file mode 100644 index af09c6e9..00000000 --- a/runtime/devnet/rustc-ice-2024-07-20T20_16_19-33192.txt +++ /dev/null @@ -1,67 +0,0 @@ -thread 'rustc' panicked at compiler/rustc_middle/src/util/bug.rs:35:44: -Box -stack backtrace: - 0: 0x101b39474 - std::backtrace::Backtrace::create::h307d17d5fb2cc0d0 - 1: 0x10ae51494 - as core[f61c09f58d048be3]::ops::function::Fn<(&dyn for<'a, 'b> core[f61c09f58d048be3]::ops::function::Fn<(&'a core[f61c09f58d048be3]::panic::panic_info::PanicInfo<'b>,), Output = ()> + core[f61c09f58d048be3]::marker::Send + core[f61c09f58d048be3]::marker::Sync, &core[f61c09f58d048be3]::panic::panic_info::PanicInfo)>>::call - 2: 0x101b52074 - std::panicking::rust_panic_with_hook::h84760468187ddc85 - 3: 0x10aeb0e28 - std[73ec08d302f978e7]::panicking::begin_panic::::{closure#0} - 4: 0x10aeb0ddc - std[73ec08d302f978e7]::sys_common::backtrace::__rust_end_short_backtrace::::{closure#0}, !> - 5: 0x10ee0858c - std[73ec08d302f978e7]::panicking::begin_panic:: - 6: 0x10aecc478 - ::emit_producing_guarantee - 7: 0x10b906c3c - rustc_middle[7d6fe3bd18d758b7]::util::bug::opt_span_bug_fmt::::{closure#0} - 8: 0x10b8ff0f4 - rustc_middle[7d6fe3bd18d758b7]::ty::context::tls::with_opt::::{closure#0}, !>::{closure#0} - 9: 0x10b8ff0c0 - rustc_middle[7d6fe3bd18d758b7]::ty::context::tls::with_context_opt::::{closure#0}, !>::{closure#0}, !> - 10: 0x10ee9d3cc - rustc_middle[7d6fe3bd18d758b7]::util::bug::bug_fmt - 11: 0x10b8fcb68 - >::fold_ty - 12: 0x10bef26fc - ::ctor_sub_tys - 13: 0x10bef95d4 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_exhaustiveness_and_usefulness:: - 14: 0x10bef9e28 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_exhaustiveness_and_usefulness:: - 15: 0x10bef9e28 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_exhaustiveness_and_usefulness:: - 16: 0x10befc130 - rustc_pattern_analysis[1c40ec00ea6b64ed]::usefulness::compute_match_usefulness:: - 17: 0x10bf19bc4 - rustc_pattern_analysis[1c40ec00ea6b64ed]::analyze_match - 18: 0x10b9e79a8 - ::analyze_patterns - 19: 0x10b9ec200 - ::visit_expr - 20: 0x10b9ec0e0 - ::visit_expr - 21: 0x10b9ec49c - ::visit_expr - 22: 0x10b9ec0e0 - ::visit_expr - 23: 0x10b9e6a00 - rustc_mir_build[8d95155ec7b238f2]::thir::pattern::check_match::check_match - 24: 0x10c017140 - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> - 25: 0x10c0d1e98 - >::call_once - 26: 0x10bfc8334 - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, true, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> - 27: 0x10c0fcdd8 - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::check_match::get_query_non_incr::__rust_end_short_backtrace - 28: 0x10b9850d0 - rustc_mir_build[8d95155ec7b238f2]::build::mir_built - 29: 0x10c01c100 - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> - 30: 0x10c094e28 - >::call_once - 31: 0x10bfcc0b4 - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> - 32: 0x10c0ef058 - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::mir_built::get_query_non_incr::__rust_end_short_backtrace - 33: 0x10ba1d4d4 - rustc_mir_build[8d95155ec7b238f2]::check_unsafety::check_unsafety - 34: 0x10c0179a4 - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> - 35: 0x10c059dfc - >::call_once - 36: 0x10bfc4b3c - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> - 37: 0x10c0f6d54 - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::check_unsafety::get_query_non_incr::__rust_end_short_backtrace - 38: 0x10b5b83fc - std[73ec08d302f978e7]::panicking::try::<(), core[f61c09f58d048be3]::panic::unwind_safe::AssertUnwindSafe::par_body_owners::{closure#0}>::{closure#0}::{closure#0}::{closure#0}>> - 39: 0x10b56c9f8 - rustc_data_structures[ff694425d12ac56b]::sync::parallel::disabled::par_for_each_in::<&[rustc_span[8def9120bc3fdd6f]::def_id::LocalDefId], ::par_body_owners::{closure#0}> - 40: 0x10b5b6448 - ::time::<(), rustc_interface[2352a4bab4f6367d]::passes::analysis::{closure#1}> - 41: 0x10b5614fc - rustc_interface[2352a4bab4f6367d]::passes::analysis - 42: 0x10c01bdac - rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::__rust_begin_short_backtrace::> - 43: 0x10c0d47d0 - >::call_once - 44: 0x10bf83e54 - rustc_query_system[4a271ab9f88cfcb9]::query::plumbing::try_execute_query::>, false, false, false>, rustc_query_impl[cc9fd01c5e6afd2e]::plumbing::QueryCtxt, false> - 45: 0x10c0ebcec - rustc_query_impl[cc9fd01c5e6afd2e]::query_impl::analysis::get_query_non_incr::__rust_end_short_backtrace - 46: 0x10ae149f0 - ::enter::> - 47: 0x10ae13c18 - ::enter::, rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>> - 48: 0x10adffe80 - rustc_span[8def9120bc3fdd6f]::create_session_globals_then::, rustc_interface[2352a4bab4f6367d]::interface::run_compiler, rustc_driver_impl[e71547a46cb79a13]::run_compiler::{closure#0}>::{closure#0}> - 49: 0x10ae1182c - std[73ec08d302f978e7]::sys_common::backtrace::__rust_begin_short_backtrace::, rustc_driver_impl[e71547a46cb79a13]::run_compiler::{closure#0}>::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>> - 50: 0x10ae08230 - <::spawn_unchecked_, rustc_driver_impl[e71547a46cb79a13]::run_compiler::{closure#0}>::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>>::{closure#0}::{closure#0}, core[f61c09f58d048be3]::result::Result<(), rustc_span[8def9120bc3fdd6f]::ErrorGuaranteed>>::{closure#1} as core[f61c09f58d048be3]::ops::function::FnOnce<()>>::call_once::{shim:vtable#0} - 51: 0x101b5a5a8 - std::sys::pal::unix::thread::Thread::new::thread_start::h9266fbbdd0c3d8be - 52: 0x1898def94 - __pthread_joiner_wake - - -rustc version: 1.78.0 (9b00956e5 2024-04-29) -platform: aarch64-apple-darwin - -query stack during panic: -#0 [check_match] match-checking `::contains` -#1 [mir_built] building MIR for `::contains` -#2 [check_unsafety] unsafety-checking `::contains` -#3 [analysis] running analysis passes on this crate -end of query stack diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 785e76be..3d727786 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -1,42 +1,35 @@ +mod v0; + +use crate::{ + config::assets::TrustBackedAssetsInstance, + fungibles::{ + self, + FungiblesKey::{self, *}, + }, + state_keys::RuntimeStateKeys, + AccountId, AllowedApiCalls, RuntimeCall, RuntimeOrigin, +}; use codec::{Decode, Encode}; use frame_support::{ dispatch::{GetDispatchInfo, RawOrigin}, pallet_prelude::*, - traits::{ - fungibles::{approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect}, - Contains, OriginTrait, - }, + traits::{Contains, OriginTrait}, }; use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, }; +use primitives::AssetId; use sp_core::crypto::UncheckedFrom; use sp_runtime::{traits::Dispatchable, DispatchError}; use sp_std::vec::Vec; -use crate::{ - config::assets::TrustBackedAssetsInstance, - fungibles::{ - self, - Keys::{self, *}, - }, - state_keys::RuntimeStateKeys, - AccountId, AllowedApiCalls, Balance, Runtime, RuntimeCall, RuntimeOrigin, UNIT, -}; -use primitives::AssetId; - -mod v0; - const LOG_TARGET: &str = "pop-api::extension"; -// Versions: -const V0: u8 = 0; type ContractSchedule = ::Schedule; #[derive(Default)] pub struct PopApiExtension; -// TODO: check removal or simplification of trait bounds. impl ChainExtension for PopApiExtension where T: pallet_contracts::Config @@ -114,18 +107,17 @@ where { const LOG_PREFIX: &str = " dispatch |"; - // Prefix params with version, pallet, index to simplify decoding + // Prefix params with version, pallet, index to simplify decoding. params.insert(0, version); params.insert(1, pallet_index); params.insert(2, call_index); - - let call = ::decode(&mut ¶ms[..]) + let call = ::decode(&mut ¶ms[..]) .map_err(|_| DispatchError::Other("DecodingFailed"))?; // Contract is the origin by default. let origin: RuntimeOrigin = RawOrigin::Signed(env.ext().address().clone()).into(); match call { - VersionedRuntimeCall::V0(call) => dispatch_call::(env, call, origin, LOG_PREFIX), + VersionedDispatch::V0(call) => dispatch_call::(env, call, origin, LOG_PREFIX), } } @@ -175,16 +167,15 @@ where { const LOG_PREFIX: &str = " read_state |"; - // Prefix params with version, pallet, index to simplify decoding + // Prefix params with version, pallet, index to simplify decoding. params.insert(0, version); params.insert(1, pallet_index); params.insert(2, call_index); - - let key = >::decode(&mut ¶ms[..]) + let key = >::decode(&mut ¶ms[..]) .map_err(|_| DispatchError::Other("DecodingFailed"))?; let result = match key { - VersionedRuntimeStateKeys::V0(key) => match key { + VersionedStateRead::V0(key) => match key { RuntimeStateKeys::Fungibles(key) => read_fungibles_state::(key, env), }, }? @@ -196,16 +187,16 @@ where env.write(&result, false, None) } -// Example wrapper to enable versioning of state read keys -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -enum VersionedRuntimeStateKeys { +// Example wrapper to enable versioning of `RuntimeStateKeys`. +#[derive(Decode, Debug)] +enum VersionedStateRead { #[codec(index = 0)] V0(RuntimeStateKeys), } -// Example wrapper to enable versioning of state read keys +// Wrapper to enable versioning of `RuntimeCall`. #[derive(Decode, Debug)] -enum VersionedRuntimeCall { +enum VersionedDispatch { #[codec(index = 0)] V0(RuntimeCall), } @@ -250,7 +241,7 @@ pub(crate) fn convert_to_status_code(error: DispatchError, version: u8) -> u32 { // - Represents the second level of nesting in `DOUBLE_NESTED_ERRORS`. // - Byte 3: // - Unused or represents further nested information. - 0 => v0::error::handle_unknown_error(&mut encoded_error), + 0 => v0::handle_unknown_error(&mut encoded_error), _ => encoded_error = [254, 0, 0, 0], } u32::from_le_bytes(encoded_error) @@ -291,7 +282,7 @@ impl TryFrom for FuncId { } fn read_fungibles_state( - key: Keys, + key: FungiblesKey, env: &mut Environment, ) -> Result, DispatchError> where diff --git a/runtime/devnet/src/extensions/v0/error.rs b/runtime/devnet/src/extensions/v0.rs similarity index 100% rename from runtime/devnet/src/extensions/v0/error.rs rename to runtime/devnet/src/extensions/v0.rs diff --git a/runtime/devnet/src/extensions/v0/mod.rs b/runtime/devnet/src/extensions/v0/mod.rs deleted file mode 100644 index cf0627b1..00000000 --- a/runtime/devnet/src/extensions/v0/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub(crate) mod error; diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index d198025a..485fb41d 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -259,7 +259,11 @@ impl Contains for AllowedApiCalls { use fungibles::Call as FungiblesCall; matches!( c, - RuntimeCall::Fungibles(FungiblesCall::transfer { .. } | FungiblesCall::approve { .. }) + RuntimeCall::Fungibles( + FungiblesCall::transfer { .. } + | FungiblesCall::approve { .. } + | FungiblesCall::increase_allowance { .. } + ) ) } } @@ -975,19 +979,18 @@ cumulus_pallet_parachain_system::register_validate_block! { pub(crate) mod state_keys { use super::fungibles; use codec::{Decode, Encode, MaxEncodedLen}; - use primitives::constants::FUNGIBLES; #[derive(Encode, Decode, Debug, MaxEncodedLen)] #[repr(u8)] pub enum RuntimeStateKeys { - Fungibles(fungibles::Keys) = FUNGIBLES, + #[codec(index = 150)] + Fungibles(fungibles::FungiblesKey), } } #[test] fn check_encoding() { use codec::{Compact, Encode}; - use primitives::constants::FUNGIBLES; use sp_runtime::{AccountId32, MultiAddress}; let id = Compact(5u32); @@ -999,6 +1002,6 @@ fn check_encoding() { value, }) .encode(); - let encoded = (FUNGIBLES, 10u8, id, spender, value).encode(); + let encoded = (150u8, 10u8, id, spender, value).encode(); assert_eq!(encoded_runtime_call, encoded); } From 43ae445ce255595aeb3d6c2a77893f52dabfa539 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 22 Jul 2024 17:17:01 +0200 Subject: [PATCH 06/56] feat: add fungibles pallet functions --- Cargo.lock | 187 +++++++++--------- pallets/api/Cargo.toml | 2 + pallets/api/src/fungibles/mod.rs | 32 ++- pallets/api/src/fungibles/tests.rs | 140 +++++++++++-- pallets/api/src/lib.rs | 2 + pallets/api/src/mock.rs | 90 +++++++-- .../integration-tests/src/local_fungibles.rs | 6 +- pop-api/src/v0/assets/fungibles.rs | 124 ++++++------ runtime/devnet/src/lib.rs | 18 -- runtime/testnet/src/extensions.rs | 10 +- 10 files changed, 394 insertions(+), 217 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3b200f43..41be62b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -215,7 +215,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -361,9 +361,9 @@ checksum = "5d5dde061bd34119e902bbb2d9b90c5692635cf59fb91d582c2b68043f1b8293" [[package]] name = "arrayref" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "9d151e35f61089500b617991b791fc8bfd237ae50cd5950803758a179b41e67a" [[package]] name = "arrayvec" @@ -740,9 +740,9 @@ dependencies = [ [[package]] name = "async-signal" -version = "0.2.8" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "794f185324c2f00e771cd9f1ae8b5ac68be2ca7abb129a87afd6e86d228bc54d" +checksum = "dfb3634b73397aa844481f814fad23bbf07fdb0eabec10f2eb95e58944b1ec32" dependencies = [ "async-io 2.3.3", "async-lock 3.4.0", @@ -770,7 +770,7 @@ checksum = "6e0c28dcc82d7c8ead5cb13beb15405b57b8546e93215673ff8ca0349a028107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -907,7 +907,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1457,9 +1457,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.1.5" +version = "1.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "324c74f2155653c90b04f25b2a47a8a631360cb908f92a772695f430c7e31052" +checksum = "2aba8f4e9906c7ce3c73463f62a7f0c65183ada1a2d47e397cc8810827f9694f" dependencies = [ "jobserver", "libc", @@ -1628,7 +1628,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -1676,7 +1676,7 @@ dependencies = [ "nom", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2342,7 +2342,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2662,7 +2662,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2702,7 +2702,7 @@ dependencies = [ "proc-macro2", "quote", "scratch", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2719,7 +2719,7 @@ checksum = "4b2c1c1776b986979be68bb2285da855f8d8a35851a769fca8740df7c3d07877" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2767,7 +2767,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2789,7 +2789,7 @@ checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" dependencies = [ "darling_core 0.20.10", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2881,7 +2881,7 @@ checksum = "d65d7ce8132b7c0e54497a4d9a55a1c2a0912a0d786cf894472ba818fba45762" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2894,7 +2894,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -2983,7 +2983,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3007,9 +3007,9 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.71", + "syn 2.0.72", "termcolor", - "toml 0.8.14", + "toml 0.8.15", "walkdir", ] @@ -3219,7 +3219,7 @@ checksum = "de0d48a183585823424a4ce1aa132d174a6a81bd540895822eb4c8373a8e49e8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3230,7 +3230,7 @@ checksum = "6fd000fd6988e73bbe993ea3db9b1aa64906ab88766d654973924340c8cddb42" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3413,7 +3413,7 @@ dependencies = [ "prettyplease 0.2.20", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3688,7 +3688,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3849,7 +3849,7 @@ dependencies = [ "proc-macro2", "quote", "sp-crypto-hashing", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3862,7 +3862,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -3873,7 +3873,7 @@ checksum = "68672b9ec6fe72d259d3879dc212c5e42e977588cdac830c76f54d9f492aeb58" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -4066,7 +4066,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5197,9 +5197,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "4979f22fdb869068da03c9f7528f8297c6fd2606bc3a4affe42e6a823fdb8da4" dependencies = [ "cfg-if", "windows-targets 0.52.6", @@ -5847,7 +5847,7 @@ dependencies = [ "macro_magic_core", "macro_magic_macros", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5861,7 +5861,7 @@ dependencies = [ "macro_magic_core_macros", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5872,7 +5872,7 @@ checksum = "b02abfe41815b5bd98dbd4260173db2c116dda171dc0fe7838cb206333b83308" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -5883,7 +5883,7 @@ checksum = "73ea28ee64b88876bf45277ed9a5817c1817df061a74f2b988971a12570e5869" dependencies = [ "macro_magic_core", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6245,7 +6245,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "synstructure 0.13.1", ] @@ -6293,7 +6293,7 @@ checksum = "254a5372af8fc138e36684761d3c0cdb758a4410e938babcff1c860ce14ddbfc" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -6624,6 +6624,7 @@ dependencies = [ "frame-support", "frame-system", "pallet-assets", + "pallet-balances", "parity-scale-codec", "primitives", "scale-info", @@ -7103,7 +7104,7 @@ checksum = "3163c6bc21b55a0ccb74c546ba784d9c9e69beb9240c059d28a3052f4cbce509" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -7771,7 +7772,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8361,7 +8362,7 @@ dependencies = [ "pest_meta", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -8402,7 +8403,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9576,7 +9577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6380dbe1fb03ecc74ad55d841cfc75480222d153ba69ddcb00977866cbdabdb8" dependencies = [ "polkavm-derive-impl", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9588,7 +9589,7 @@ dependencies = [ "polkavm-common", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -9872,9 +9873,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0" +checksum = "da544ee218f0d287a911e9c99a39a8c9bc8fcad3cb8db5959940044ecfc67265" [[package]] name = "powerfmt" @@ -9935,7 +9936,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10038,7 +10039,7 @@ checksum = "834da187cfe638ae8abb0203f0b33e5ccdb02a28e7199f2f47b3e2754f50edca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10084,7 +10085,7 @@ checksum = "440f724eba9f6996b75d63681b0a92b06947f1457076d503a4d2e2c8f56442b8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10152,7 +10153,7 @@ dependencies = [ "itertools 0.12.1", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -10418,7 +10419,7 @@ checksum = "bcc303e793d3734489387d205e9b186fac9c6cfacedd98cbb2e8a5943595f3e6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -11079,7 +11080,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -12093,7 +12094,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -12259,7 +12260,7 @@ dependencies = [ "proc-macro2", "quote", "scale-info", - "syn 2.0.71", + "syn 2.0.72", "thiserror", ] @@ -12492,7 +12493,7 @@ checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13111,7 +13112,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13377,7 +13378,7 @@ checksum = "b85d0f1f1e44bd8617eb2a48203ee854981229e3e79e6f468c7175d5fd37489b" dependencies = [ "quote", "sp-crypto-hashing", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13398,7 +13399,7 @@ checksum = "48d09fa0a5f7299fb81ee25ae3853d26200f7a348148aed6de76be905c007dbe" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13647,7 +13648,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -13854,7 +13855,7 @@ dependencies = [ "parity-scale-codec", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14140,7 +14141,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14241,7 +14242,7 @@ dependencies = [ "sp-maybe-compressed-blob", "strum 0.24.1", "tempfile", - "toml 0.8.14", + "toml 0.8.15", "walkdir", "wasm-opt", ] @@ -14316,7 +14317,7 @@ dependencies = [ "scale-info", "scale-typegen", "subxt-metadata", - "syn 2.0.71", + "syn 2.0.72", "thiserror", "tokio", ] @@ -14350,7 +14351,7 @@ dependencies = [ "quote", "scale-typegen", "subxt-codegen", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14401,9 +14402,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "dc4b9b9bf2add8093d3f2c0204471e951b2285580335de42f9d2534f3ae7a8af" dependencies = [ "proc-macro2", "quote", @@ -14430,7 +14431,7 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14520,9 +14521,9 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c0342370b38b6a11b6cc11d6a805569958d54cfa061a29969c3b5ce2ea405724" dependencies = [ "thiserror-impl", ] @@ -14544,18 +14545,18 @@ checksum = "e4c60d69f36615a077cc7663b9cb8e42275722d23e58a7fa3d2c7f2915d09d04" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "a4558b58466b9ad7ca0f102865eccc95938dca1a74a856f2b57b6629050da261" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14674,9 +14675,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.38.0" +version = "1.38.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba4f4a02a7a80d6f274636f0aa95c7e383b912d41fe721a31f29e29698585a4a" +checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" dependencies = [ "backtrace", "bytes", @@ -14699,7 +14700,7 @@ checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14771,14 +14772,14 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.14" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f49eb2ab21d2f26bd6db7bf383edc527a7ebaee412d17af4d40fdccd442f335" +checksum = "ac2caab0bf757388c6c0ae23b3293fdb463fee59434529014f85e3263b995c28" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.15", + "toml_edit 0.22.16", ] [[package]] @@ -14814,15 +14815,15 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.22.15" +version = "0.22.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d59a3a72298453f564e2b111fa896f8d07fabb36f51f06d7e875fc5e0b5a3ef1" +checksum = "278f3d518e152219c994ce877758516bca5e118eaed6996192a774fb9fbf0788" dependencies = [ "indexmap 2.2.6", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.13", + "winnow 0.6.14", ] [[package]] @@ -14890,7 +14891,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -14935,7 +14936,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -15361,7 +15362,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-shared", ] @@ -15395,7 +15396,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -15883,9 +15884,9 @@ dependencies = [ [[package]] name = "wide" -version = "0.7.25" +version = "0.7.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2caba658a80831539b30698ae9862a72db6697dfdd7151e46920f5f2755c3ce2" +checksum = "901e8597c777fa042e9e245bd56c0dc4418c5db3f845b6ff94fbac732c6a0692" dependencies = [ "bytemuck", "safe_arch", @@ -16172,9 +16173,9 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.13" +version = "0.6.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59b5e5f6c299a3c7890b876a2a587f3115162487e704907d9b6cd29473052ba1" +checksum = "374ec40a2d767a3c1b4972d9475ecd557356637be906f2cb3f7fe17a6eb5e22f" dependencies = [ "memchr", ] @@ -16283,7 +16284,7 @@ dependencies = [ "Inflector", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -16332,7 +16333,7 @@ checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] @@ -16352,7 +16353,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.72", ] [[package]] diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 0e87e744..2479bdd0 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -22,6 +22,7 @@ primitives = { workspace = true, features = ["fungibles"] } sp-runtime.workspace = true [dev-dependencies] +pallet-balances.workspace = true sp-core.workspace = true sp-io.workspace = true @@ -39,6 +40,7 @@ std = [ "frame-support/std", "frame-system/std", "pallet-assets/std", + "pallet-balances/std", "primitives/std", "scale-info/std", "sp-core/std", diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 32ec76c0..d82946d4 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -16,10 +16,10 @@ pub mod pallet { }; use frame_system::pallet_prelude::*; use pallet_assets::WeightInfo; - use sp_runtime::traits::StaticLookup; + use sp_runtime::{traits::StaticLookup, Saturating}; - type AccountIdOf = ::AccountId; - type AssetIdOf = > as Inspect< + pub(crate) type AccountIdOf = ::AccountId; + pub(crate) type AssetIdOf = > as Inspect< ::AccountId, >>::AssetId; type AssetIdParameterOf = @@ -27,11 +27,11 @@ pub mod pallet { type Assets = pallet_assets::Pallet>; type AssetsInstanceOf = ::AssetsInstance; type AssetsWeightInfo = >>::WeightInfo; - type BalanceOf = > as Inspect< + pub(crate) type BalanceOf = > as Inspect< ::AccountId, >>::Balance; - /// The required input for state queries in pallet assets. + /// The required input for state queries through the fungibles api. #[derive(Encode, Decode, Debug, MaxEncodedLen)] #[repr(u8)] #[allow(clippy::unnecessary_cast)] @@ -78,13 +78,27 @@ pub mod pallet { origin: OriginFor, id: AssetIdOf, spender: AccountIdOf, - value: BalanceOf, + mut value: BalanceOf, ) -> DispatchResultWithPostInfo { + let who = ensure_signed(origin.clone())?; + let current_allowance = Assets::::allowance(id.clone(), &who, &spender); let spender = T::Lookup::unlookup(spender); let id: AssetIdParameterOf = id.into(); - Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) - .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; - Assets::::approve_transfer(origin, id, spender, value)?; + // If the new value is equal to the current allowance, do nothing. + if value == current_allowance { + return Ok(().into()); + } + // If the new value is greater than the current allowance, approve the difference. + if value > current_allowance { + value.saturating_reduce(current_allowance); + Assets::::approve_transfer(origin, id, spender, value)?; + } else { + // If the new value is less than the current allowance, cancel the approval and set the new value + Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; + Assets::::approve_transfer(origin, id, spender, value)?; + } + Ok(().into()) } diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index a5116a72..185e363b 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -1,22 +1,140 @@ -use crate::{mock::*, Error}; -use frame_support::{assert_noop, assert_ok}; +use crate::mock::*; +use frame_support::{ + assert_ok, + traits::fungibles::{approvals::Inspect, metadata::Inspect as MetadataInspect}, +}; -// https://docs.substrate.io/test/ +const ASSET: u32 = 42; #[test] -fn it_works_for_default_value() { +fn transfer_works() { new_test_ext().execute_with(|| { - // Dispatch a signed extrinsic. - assert_ok!(fungibles::do_something(RuntimeOrigin::signed(1), 42)); - // Read pallet storage and assert an expected result. - assert_eq!(fungibles::something(), Some(42)); + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); + let bob_balance_before_transfer = Assets::balance(ASSET, &BOB); + assert_ok!(Fungibles::transfer(signed(ALICE), ASSET, BOB, amount / 2)); + let bob_balance_after_transfer = Assets::balance(ASSET, &BOB); + assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + amount / 2); }); } +// Non-additive, sets new value. #[test] -fn correct_error_for_none_value() { +fn approve_works() { new_test_ext().execute_with(|| { - // Ensure the expected error is thrown when no value is present. - assert_noop!(fungibles::cause_error(RuntimeOrigin::signed(1)), Error::::NoneValue); + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); + assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + // Sets a new allowance amount that is lower than the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount / 2)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount / 2); + // Sets a new allowance amount that is higher than the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); }); } + +#[test] +fn increase_allowance_works() { + new_test_ext().execute_with(|| { + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); + assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); + assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + }); +} + +#[test] +fn total_supply_works() { + new_test_ext().execute_with(|| { + create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); + assert_eq!(Assets::total_supply(ASSET), Fungibles::total_supply(ASSET)); + }); +} + +#[test] +fn balance_of_works() { + new_test_ext().execute_with(|| { + create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); + assert_eq!(Assets::balance(ASSET, ALICE), Fungibles::balance_of(ASSET, &ALICE)); + }); +} + +#[test] +fn allowance_works() { + new_test_ext().execute_with(|| { + create_asset_mint_and_approve(ALICE, ASSET, BOB, 100, ALICE, 50); + assert_eq!( + Assets::allowance(ASSET, &ALICE, &BOB), + Fungibles::allowance(ASSET, &ALICE, &BOB) + ); + }); +} + +#[test] +fn token_metadata_works() { + new_test_ext().execute_with(|| { + let name: Vec = vec![11, 12, 13]; + let symbol: Vec = vec![21, 22, 23]; + let decimals: u8 = 69; + create_asset_and_set_metadata(ALICE, ASSET, name.clone(), symbol.clone(), decimals); + assert_eq!(Assets::name(ASSET), Fungibles::token_name(ASSET)); + assert_eq!(Assets::symbol(ASSET), Fungibles::token_symbol(ASSET)); + assert_eq!(Assets::decimals(ASSET), Fungibles::token_decimals(ASSET)); + }); +} + +fn signed(account: AccountId) -> RuntimeOrigin { + RuntimeOrigin::signed(account) +} + +fn create_asset(owner: AccountId, asset_id: AssetId, min_balance: Balance) { + assert_ok!(Assets::create(signed(owner), asset_id, owner, min_balance)); +} + +fn mint_asset(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance) { + assert_ok!(Assets::mint(signed(owner), asset_id, to, value)); +} + +fn create_asset_and_mint_to(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance) { + create_asset(owner, asset_id, 1); + mint_asset(owner, asset_id, to, value) +} + +fn create_asset_mint_and_approve( + owner: AccountId, + asset_id: AssetId, + to: AccountId, + mint: Balance, + spender: AccountId, + approve: Balance, +) { + create_asset_and_mint_to(owner, asset_id, to, mint); + assert_ok!(Assets::approve_transfer(signed(to), asset_id, spender, approve,)); +} + +fn create_asset_and_set_metadata( + owner: AccountId, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) { + assert_ok!(Assets::create(signed(owner), asset_id, owner, 100)); + set_metadata_asset(owner, asset_id, name, symbol, decimals); +} + +fn set_metadata_asset( + owner: AccountId, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) { + assert_ok!(Assets::set_metadata(signed(owner), asset_id, name, symbol, decimals)); +} diff --git a/pallets/api/src/lib.rs b/pallets/api/src/lib.rs index 3aaad4c2..5cba0551 100644 --- a/pallets/api/src/lib.rs +++ b/pallets/api/src/lib.rs @@ -1,3 +1,5 @@ #![cfg_attr(not(feature = "std"), no_std)] pub mod fungibles; +#[cfg(test)] +mod mock; diff --git a/pallets/api/src/mock.rs b/pallets/api/src/mock.rs index 82412fe3..5f0340a9 100644 --- a/pallets/api/src/mock.rs +++ b/pallets/api/src/mock.rs @@ -1,5 +1,8 @@ -use frame_support::{parameter_types, traits::Everything}; -use frame_system as system; +use frame_support::{ + derive_impl, parameter_types, + traits::{AsEnsureOriginWithArg, ConstU128, ConstU32, Everything}, +}; +use frame_system::{EnsureRoot, EnsureSigned}; use sp_core::H256; use sp_runtime::{ traits::{BlakeTwo256, IdentityLookup}, @@ -7,13 +10,18 @@ use sp_runtime::{ }; type Block = frame_system::mocking::MockBlock; +pub(crate) type AccountId = u64; +pub(crate) type AssetId = u32; +pub(crate) type Balance = u128; // Configure a mock runtime to test the pallet. frame_support::construct_runtime!( pub enum Test { - System: frame_system::{Pallet, Call, Config, Storage, Event}, - fungibles: crate::{Pallet, Call, Storage, Event}, + System: frame_system, + Assets: pallet_assets::, + Balances: pallet_balances, + Fungibles: crate::fungibles, } ); @@ -22,39 +30,93 @@ parameter_types! { pub const SS58Prefix: u8 = 42; } -#[derive_impl(frame_system::config_preludes::TestDefaultConfig)] -impl system::Config for Test { +#[derive_impl(frame_system::config_preludes::TestDefaultConfig as frame_system::DefaultConfig)] +impl frame_system::Config for Test { type BaseCallFilter = Everything; type BlockWeights = (); type BlockLength = (); - type DbWeight = (); type RuntimeOrigin = RuntimeOrigin; type RuntimeCall = RuntimeCall; type Nonce = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Block = Block; type RuntimeEvent = RuntimeEvent; type BlockHashCount = BlockHashCount; + type DbWeight = (); type Version = (); type PalletInfo = PalletInfo; - type AccountData = (); + type AccountData = pallet_balances::AccountData; type OnNewAccount = (); type OnKilledAccount = (); type SystemWeightInfo = (); type SS58Prefix = SS58Prefix; type OnSetCode = (); - type MaxConsumers = frame_support::traits::ConstU32<16>; + type MaxConsumers = ConstU32<16>; +} + +impl pallet_balances::Config for Test { + type Balance = Balance; + type DustRemoval = (); + type RuntimeEvent = RuntimeEvent; + type ExistentialDeposit = ConstU128<1>; + type AccountStore = System; + type FreezeIdentifier = (); + type MaxFreezes = ConstU32<0>; + type WeightInfo = (); + type MaxLocks = (); + type MaxReserves = (); + type ReserveIdentifier = [u8; 8]; + type RuntimeHoldReason = RuntimeHoldReason; + type RuntimeFreezeReason = RuntimeFreezeReason; } -impl crate::Config for Test { +type AssetsInstance = pallet_assets::Instance1; +impl pallet_assets::Config for Test { type RuntimeEvent = RuntimeEvent; + type Balance = Balance; + type RemoveItemsLimit = ConstU32<5>; + type AssetId = AssetId; + type AssetIdParameter = u32; + type Currency = Balances; + type CreateOrigin = AsEnsureOriginWithArg>; + type ForceOrigin = EnsureRoot; + type AssetDeposit = ConstU128<1>; + type AssetAccountDeposit = ConstU128<10>; + type MetadataDepositBase = ConstU128<1>; + type MetadataDepositPerByte = ConstU128<1>; + type ApprovalDeposit = ConstU128<1>; + type StringLimit = ConstU32<50>; + type Freezer = (); + type Extra = (); + type CallbackHandle = (); type WeightInfo = (); + #[cfg(feature = "runtime-benchmarks")] + type BenchmarkHelper = (); +} +impl crate::fungibles::Config for Test { + type AssetsInstance = AssetsInstance; } -// Build genesis storage according to the mock runtime. -pub fn new_test_ext() -> sp_io::TestExternalities { - system::GenesisConfig::::default().build_storage().unwrap().into() +pub(crate) const ALICE: AccountId = 1; +pub(crate) const BOB: AccountId = 2; +pub(crate) const INIT_AMOUNT: Balance = 100_000_000 * UNIT; +pub(crate) const UNIT: Balance = 10_000_000_000; + +pub(crate) fn new_test_ext() -> sp_io::TestExternalities { + let mut t = frame_system::GenesisConfig::::default() + .build_storage() + .expect("Frame system builds valid default genesis config"); + + pallet_balances::GenesisConfig:: { + balances: vec![(ALICE, INIT_AMOUNT), (BOB, INIT_AMOUNT)], + } + .assimilate_storage(&mut t) + .expect("Pallet balances storage can be assimilated"); + + let mut ext = sp_io::TestExternalities::new(t); + ext.execute_with(|| System::set_block_number(1)); + ext } diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index a559fb30..9afae649 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -351,11 +351,11 @@ fn transfer_works() { Module { index: 52, error: 0 }, ); // Successful transfer. - let bob_balance_before_mint = Assets::balance(asset, &BOB); + let bob_balance_before_transfer = Assets::balance(asset, &BOB); let result = transfer(addr.clone(), asset, BOB, amount / 2); assert!(!result.did_revert(), "Contract reverted!"); - let bob_balance_after_mint = Assets::balance(asset, &BOB); - assert_eq!(bob_balance_after_mint, bob_balance_before_mint + amount / 2); + let bob_balance_after_transfer = Assets::balance(asset, &BOB); + assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + amount / 2); // Transfer asset to account that does not exist. assert_eq!( decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 0539ebb0..0805393f 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -51,68 +51,6 @@ mod constants { // - clear_metadata } -/// Represents various errors related to local fungible assets in the Pop API. -/// -/// The `FungiblesError` provides a detailed and specific set of error types that can occur when -/// interacting with fungible assets through the Pop API. Each variant signifies a particular error -/// condition, facilitating precise error handling and debugging. -/// -/// It is designed to be lightweight, including only the essential errors relevant to fungible asset -/// operations. The `Other` variant serves as a catch-all for any unexpected errors. For more -/// detailed debugging, the `Other` variant can be converted into the richer `Error` type defined in -/// the primitives crate. -#[derive(Debug, PartialEq, Eq)] -#[ink::scale_derive(Encode, Decode, TypeInfo)] -pub enum FungiblesError { - /// An unspecified or unknown error occurred. - Other(StatusCode), - /// The asset is not live; either frozen or being destroyed. - AssetNotLive, - /// Not enough allowance to fulfill a request is available. - InsufficientAllowance, - /// Not enough balance to fulfill a request is available. - InsufficientBalance, - /// The asset ID is already taken. - InUse, - /// Minimum balance should be non-zero. - MinBalanceZero, - /// The account to alter does not exist. - NoAccount, - /// The signing account has no permission to do the operation. - NoPermission, - /// The given asset ID is unknown. - Unknown, - /// No balance for creation of assets or fees. - // TODO: Originally `pallet_balances::Error::InsufficientBalance` but collides with the - // `InsufficientBalance` error that is used for `pallet_assets::Error::BalanceLow` to adhere to - // standard. This deserves a second look. - NoBalance, -} - -impl From for FungiblesError { - /// Converts a `StatusCode` to a `FungiblesError`. - /// - /// This conversion maps a `StatusCode`, returned by the runtime, to a more descriptive - /// `FungiblesError`. This provides better context and understanding of the error, allowing - /// developers to handle the most important errors effectively. - fn from(value: StatusCode) -> Self { - let encoded = value.0.to_le_bytes(); - match encoded { - // Balances. - [_, BALANCES, 2, _] => FungiblesError::NoBalance, - // Assets. - [_, ASSETS, 0, _] => FungiblesError::NoAccount, - [_, ASSETS, 1, _] => FungiblesError::NoPermission, - [_, ASSETS, 2, _] => FungiblesError::Unknown, - [_, ASSETS, 3, _] => FungiblesError::InUse, - [_, ASSETS, 5, _] => FungiblesError::MinBalanceZero, - [_, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, - [_, ASSETS, 10, _] => FungiblesError::AssetNotLive, - _ => FungiblesError::Other(value), - } - } -} - /// Returns the total token supply for a given asset ID. /// /// # Arguments @@ -402,6 +340,68 @@ pub mod metadata { // assets::asset_exists(id) // } +/// Represents various errors related to local fungible assets in the Pop API. +/// +/// The `FungiblesError` provides a detailed and specific set of error types that can occur when +/// interacting with fungible assets through the Pop API. Each variant signifies a particular error +/// condition, facilitating precise error handling and debugging. +/// +/// It is designed to be lightweight, including only the essential errors relevant to fungible asset +/// operations. The `Other` variant serves as a catch-all for any unexpected errors. For more +/// detailed debugging, the `Other` variant can be converted into the richer `Error` type defined in +/// the primitives crate. +#[derive(Debug, PartialEq, Eq)] +#[ink::scale_derive(Encode, Decode, TypeInfo)] +pub enum FungiblesError { + /// An unspecified or unknown error occurred. + Other(StatusCode), + /// The asset is not live; either frozen or being destroyed. + AssetNotLive, + /// Not enough allowance to fulfill a request is available. + InsufficientAllowance, + /// Not enough balance to fulfill a request is available. + InsufficientBalance, + /// The asset ID is already taken. + InUse, + /// Minimum balance should be non-zero. + MinBalanceZero, + /// The account to alter does not exist. + NoAccount, + /// The signing account has no permission to do the operation. + NoPermission, + /// The given asset ID is unknown. + Unknown, + /// No balance for creation of assets or fees. + // TODO: Originally `pallet_balances::Error::InsufficientBalance` but collides with the + // `InsufficientBalance` error that is used for `pallet_assets::Error::BalanceLow` to adhere to + // standard. This deserves a second look. + NoBalance, +} + +impl From for FungiblesError { + /// Converts a `StatusCode` to a `FungiblesError`. + /// + /// This conversion maps a `StatusCode`, returned by the runtime, to a more descriptive + /// `FungiblesError`. This provides better context and understanding of the error, allowing + /// developers to handle the most important errors effectively. + fn from(value: StatusCode) -> Self { + let encoded = value.0.to_le_bytes(); + match encoded { + // Balances. + [_, BALANCES, 2, _] => FungiblesError::NoBalance, + // Assets. + [_, ASSETS, 0, _] => FungiblesError::NoAccount, + [_, ASSETS, 1, _] => FungiblesError::NoPermission, + [_, ASSETS, 2, _] => FungiblesError::Unknown, + [_, ASSETS, 3, _] => FungiblesError::InUse, + [_, ASSETS, 5, _] => FungiblesError::MinBalanceZero, + [_, ASSETS, 7, _] => FungiblesError::InsufficientAllowance, + [_, ASSETS, 10, _] => FungiblesError::AssetNotLive, + _ => FungiblesError::Other(value), + } + } +} + #[cfg(test)] mod tests { use ink::scale::{Decode, Encode}; diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 485fb41d..4d4ffc7c 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -987,21 +987,3 @@ pub(crate) mod state_keys { Fungibles(fungibles::FungiblesKey), } } - -#[test] -fn check_encoding() { - use codec::{Compact, Encode}; - use sp_runtime::{AccountId32, MultiAddress}; - - let id = Compact(5u32); - let spender = MultiAddress::Id(AccountId32::from([0; 32])); - let value = 100u128; - let encoded_runtime_call = RuntimeCall::Fungibles(fungibles::Call::::approve { - id, - spender: spender.clone(), - value, - }) - .encode(); - let encoded = (150u8, 10u8, id, spender, value).encode(); - assert_eq!(encoded_runtime_call, encoded); -} diff --git a/runtime/testnet/src/extensions.rs b/runtime/testnet/src/extensions.rs index d2d11598..a6e309f9 100644 --- a/runtime/testnet/src/extensions.rs +++ b/runtime/testnet/src/extensions.rs @@ -6,7 +6,6 @@ use frame_support::{ use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, ChargedAmount, Environment, Ext, InitState, RetVal, }; -use pop_primitives::storage_keys::RuntimeStateKeys; use sp_core::crypto::UncheckedFrom; use sp_runtime::{traits::Dispatchable, DispatchError}; use sp_std::vec::Vec; @@ -181,12 +180,9 @@ where log::debug!(target:LOG_TARGET, "{} charged weight: {:?}", LOG_PREFIX, charged_weight); - let key: RuntimeStateKeys = env.read_as()?; - - let result = match key { - _ => Vec::::default(), - } - .encode(); + // TODO: always returning an empty vec. Chainextension will be refactored into one for both + // runtimes before pop api implementation gets merged into main. + let result = Vec::::default().encode(); log::trace!( target:LOG_TARGET, From bbe01a10b07965591a25776b6516dac5bc03e696 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 22 Jul 2024 17:58:01 +0200 Subject: [PATCH 07/56] tests: remove transfer_from (transfer copy) and add approve --- .../integration-tests/src/local_fungibles.rs | 61 ++++++++----------- 1 file changed, 25 insertions(+), 36 deletions(-) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 9afae649..aee1fa29 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -79,19 +79,16 @@ fn transfer( result } -fn transfer_from( +fn approve( addr: AccountId32, asset_id: AssetId, - from: Option, - to: Option, + spender: AccountId32, value: Balance, - data: &[u8], ) -> ExecReturnValue { - let function = function_selector("transfer_from"); - let params = - [function, asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()] - .concat(); - bare_call(addr, params, 0).expect("should work") + let function = function_selector("approve"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result } fn increase_allowance( @@ -371,51 +368,43 @@ fn transfer_works() { } #[test] -fn transfer_from_works() { +fn approve_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![]); let amount: Balance = 100 * UNIT; + let asset = 0; + create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); + assert_eq!(decoded::(approve(addr.clone(), asset, BOB, amount)), ConsumerRemaining); + let addr = + instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); // Asset does not exist. + let asset = 1; assert_eq!( - decoded::(transfer(addr.clone(), 1, BOB, amount,)), + decoded::(approve(addr.clone(), asset, BOB, amount)), 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); + create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); // Asset is not live, i.e. frozen or being destroyed. freeze_asset(ALICE, asset); assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount,)), + decoded::(approve(addr.clone(), asset, BOB, amount)), 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 }, - ); - // Not enough balance due to ED. - assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 0 }, - ); - // Successful transfer. - let bob_balance_before_mint = Assets::balance(asset, &BOB); - let result = transfer(addr.clone(), asset, BOB, amount / 2); - assert!(!result.did_revert(), "Contract reverted!"); - let bob_balance_after_mint = Assets::balance(asset, &BOB); - assert_eq!(bob_balance_after_mint, bob_balance_before_mint + amount / 2); - // Transfer asset to account that does not exist. - assert_eq!( - decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), - Token(CannotCreate) - ); + // Successful approval. + assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); + assert!(!approve(addr.clone(), asset, BOB, amount).did_revert(), "Contract reverted!"); + assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); + // Non-additive, sets new value. + assert!(!approve(addr.clone(), asset, BOB, amount / 2).did_revert(), "Contract reverted!"); + 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!( - decoded::(transfer(addr.clone(), asset, BOB, amount / 4)), + decoded::(approve(addr.clone(), asset, BOB, amount)), Module { index: 52, error: 16 }, ); }); From 68264da7dc59546bce66b31e2ae42137152686f8 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 22 Jul 2024 18:25:33 +0200 Subject: [PATCH 08/56] refactor: primitives --- Cargo.lock | 25 ++++++------------------- pallets/api/Cargo.toml | 2 +- primitives/Cargo.toml | 11 ++++------- primitives/src/lib.rs | 5 ++--- runtime/devnet/Cargo.toml | 2 +- runtime/devnet/src/extensions/mod.rs | 2 +- runtime/devnet/src/extensions/v0.rs | 2 +- 7 files changed, 16 insertions(+), 33 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 41be62b7..54ce86e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1051,18 +1051,6 @@ dependencies = [ "piper", ] -[[package]] -name = "bounded-collections" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca548b6163b872067dc5eb82fd130c56881435e30367d2073594a3d9744120dd" -dependencies = [ - "log", - "parity-scale-codec", - "scale-info", - "serde", -] - [[package]] name = "bounded-collections" version = "0.2.0" @@ -2383,7 +2371,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa22d6e479a4d3a2790bab291269ba0917a1ac384255a54a2ebc3f7c37e505e" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "bp-xcm-bridge-hub-router", "cumulus-primitives-core", "frame-benchmarking", @@ -8010,7 +7998,7 @@ version = "8.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba9138b04168b07b1aff4a2079f5514753c31dddba40e5fb471b9cda7da27ad6" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "frame-benchmarking", "frame-support", "frame-system", @@ -9220,7 +9208,7 @@ version = "7.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248ab090959a92e61493277e33b7e85104280a4beb4cb0815137d3c8c50a07f4" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "derive_more", "parity-scale-codec", "polkadot-core-primitives", @@ -9958,7 +9946,6 @@ dependencies = [ name = "primitives" version = "0.0.0" dependencies = [ - "bounded-collections 0.1.9", "parity-scale-codec", "scale-info", ] @@ -13306,7 +13293,7 @@ dependencies = [ "bip39", "bitflags 1.3.2", "blake2 0.10.6", - "bounded-collections 0.2.0", + "bounded-collections", "bs58 0.5.1", "dyn-clonable", "ed25519-zebra 3.1.0", @@ -13878,7 +13865,7 @@ version = "28.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3be30aec904994451dcacf841a9168cfbbaf817de6b24b6a1c1418cbf1af2fe" dependencies = [ - "bounded-collections 0.2.0", + "bounded-collections", "parity-scale-codec", "scale-info", "serde", @@ -13987,7 +13974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48fa328b87de3466bc38cc9a07244c42c647b7755b81115e1dfeb47cc13fc6e6" dependencies = [ "array-bytes 6.2.3", - "bounded-collections 0.2.0", + "bounded-collections", "derivative", "environmental", "impl-trait-for-tuples", diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 2479bdd0..d62bf641 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -18,7 +18,7 @@ frame-benchmarking.workspace = true frame-support.workspace = true frame-system.workspace = true pallet-assets.workspace = true -primitives = { workspace = true, features = ["fungibles"] } +primitives.workspace = true sp-runtime.workspace = true [dev-dependencies] diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index a12c9363..cb62ddc0 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -5,15 +5,12 @@ version = "0.0.0" edition = "2021" [dependencies] -bounded-collections = { version = "0.1", default-features = false } -scale = { package = "parity-scale-codec", version = "3", default-features = false, features = ["derive"] } -scale-info = { version = "2.10", default-features = false, features = ["derive"], optional = true } +codec.workspace = true +scale-info.workspace = true [features] default = ["std"] std = [ - "bounded-collections/std", - "scale/std", + "codec/std", "scale-info/std", -] -fungibles = [] \ No newline at end of file +] \ No newline at end of file diff --git a/primitives/src/lib.rs b/primitives/src/lib.rs index e29f933c..a51661ea 100644 --- a/primitives/src/lib.rs +++ b/primitives/src/lib.rs @@ -1,7 +1,6 @@ #![cfg_attr(not(feature = "std"), no_std, no_main)] -pub use bounded_collections::{BoundedBTreeMap, BoundedBTreeSet, BoundedVec}; -use scale::{Decode, Encode, MaxEncodedLen}; +use codec::{Decode, Encode}; #[cfg(feature = "std")] use scale_info::TypeInfo; pub use v0::error; @@ -79,7 +78,7 @@ pub mod v0 { } /// Description of what went wrong when trying to complete an operation on a token. - #[derive(Encode, Decode, Clone, Debug, MaxEncodedLen, Eq, PartialEq, Ord, PartialOrd)] + #[derive(Encode, Decode, Debug, Eq, PartialEq)] #[cfg_attr(feature = "std", derive(TypeInfo))] pub enum TokenError { /// Funds are unavailable. diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 80a0fe48..4a31f6f6 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -22,7 +22,7 @@ scale-info.workspace = true smallvec.workspace = true # Local -primitives = { workspace = true, features = ["fungibles"] } +primitives.workspace = true pop-runtime-common.workspace = true pallet-api.workspace = true diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 3d727786..4496c458 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -204,7 +204,7 @@ enum VersionedDispatch { // Converts a `DispatchError` to a `u32` status code based on the version of the API the contract uses. // The contract calling the chain extension can convert the status code to the descriptive `Error`. // -// For `Error` see `pop_primitives::::error::Error`. +// For `Error` see `primitives::::error::Error`. // // The error encoding can vary per version, allowing for flexible and backward-compatible error handling. // As a result, contracts maintain compatibility across different versions of the runtime. diff --git a/runtime/devnet/src/extensions/v0.rs b/runtime/devnet/src/extensions/v0.rs index b26668f7..d9a40264 100644 --- a/runtime/devnet/src/extensions/v0.rs +++ b/runtime/devnet/src/extensions/v0.rs @@ -60,7 +60,7 @@ fn nested_errors(nested_error: &[u8], limit: Option) -> bool { #[cfg(test)] mod tests { use super::*; - use pop_primitives::error::{ + use primitives::error::{ ArithmeticError::*, Error::{self, *}, TokenError::*, From 2481773f6a0bffcf5cd39817022273fbf2c43022 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 22 Jul 2024 21:50:15 +0200 Subject: [PATCH 09/56] fix: pallet api error + missing docs --- Cargo.lock | 1 + pallets/api/Cargo.toml | 2 + pallets/api/src/fungibles/mod.rs | 101 ++++++++++++++++++++++++++++--- 3 files changed, 97 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 54ce86e7..c0dfbaac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6619,6 +6619,7 @@ dependencies = [ "sp-core", "sp-io", "sp-runtime", + "sp-std", ] [[package]] diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index d62bf641..3f462a55 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -20,6 +20,7 @@ frame-system.workspace = true pallet-assets.workspace = true primitives.workspace = true sp-runtime.workspace = true +sp-std.workspace = true [dev-dependencies] pallet-balances.workspace = true @@ -46,6 +47,7 @@ std = [ "sp-core/std", "sp-io/std", "sp-runtime/std", + "sp-std/std", ] try-runtime = [ "frame-support/try-runtime", diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index d82946d4..221da379 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -1,5 +1,6 @@ -#![cfg_attr(not(feature = "std"), no_std)] - +/// The fungibles pallet serves as a wrapper around the pallet_assets, offering a streamlined +/// interface for interacting with fungible assets. The goal is to provide a simplified, consistent +/// API that adheres to standards in the smart contract space. pub use pallet::*; #[cfg(test)] @@ -17,6 +18,7 @@ pub mod pallet { use frame_system::pallet_prelude::*; use pallet_assets::WeightInfo; use sp_runtime::{traits::StaticLookup, Saturating}; + use sp_std::vec::Vec; pub(crate) type AccountIdOf = ::AccountId; pub(crate) type AssetIdOf = > as Inspect< @@ -60,6 +62,16 @@ pub mod pallet { #[pallet::call] impl Pallet { + /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional + /// `data` in unspecified format. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `to` - The recipient account. + /// * `value` - The number of tokens to transfer. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the transfer fails. #[pallet::call_index(0)] #[pallet::weight(AssetsWeightInfo::::transfer_keep_alive())] pub fn transfer( @@ -72,15 +84,25 @@ pub mod pallet { Assets::::transfer_keep_alive(origin, id.into(), target, amount) } + /// Approves an account to spend a specified number of tokens on behalf of the caller. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `spender` - The account that is allowed to spend the tokens. + /// * `value` - The number of tokens to approve. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the approval fails. #[pallet::call_index(2)] - #[pallet::weight(AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] + #[pallet::weight(T::DbWeight::get().reads(2) + AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] pub fn approve( origin: OriginFor, id: AssetIdOf, spender: AccountIdOf, mut value: BalanceOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin.clone())?; + let who = ensure_signed(origin.clone()) + .map_err(|e| e.with_weight(T::DbWeight::get().reads(1)))?; let current_allowance = Assets::::allowance(id.clone(), &who, &spender); let spender = T::Lookup::unlookup(spender); let id: AssetIdParameterOf = id.into(); @@ -91,17 +113,35 @@ pub mod pallet { // If the new value is greater than the current allowance, approve the difference. if value > current_allowance { value.saturating_reduce(current_allowance); - Assets::::approve_transfer(origin, id, spender, value)?; + Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { + e.with_weight( + T::DbWeight::get().reads(2) + AssetsWeightInfo::::approve_transfer(), + ) + })?; } else { // If the new value is less than the current allowance, cancel the approval and set the new value - Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()) - .map_err(|e| e.with_weight(AssetsWeightInfo::::cancel_approval()))?; + Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()).map_err( + |e| { + e.with_weight( + T::DbWeight::get().reads(2) + AssetsWeightInfo::::cancel_approval(), + ) + }, + )?; Assets::::approve_transfer(origin, id, spender, value)?; } Ok(().into()) } + /// Increases the allowance of a spender. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `spender` - The account that is allowed to spend the tokens. + /// * `value` - The number of tokens to increase the allowance by. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. #[pallet::call_index(3)] #[pallet::weight(AssetsWeightInfo::::approve_transfer())] pub fn increase_allowance( @@ -116,14 +156,40 @@ pub mod pallet { } impl Pallet { + /// Returns the total token supply for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The total supply of the token, or an error if the operation fails. pub fn total_supply(id: AssetIdOf) -> BalanceOf { Assets::::total_supply(id) } + /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if + /// the account is non-existent. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `owner` - The account whose balance is being queried. + /// + /// # Returns + /// The balance of the specified account, or an error if the operation fails. pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { Assets::::balance(id, owner) } + /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given + /// asset ID. Returns `0` if no allowance has been set. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `owner` - The account that owns the tokens. + /// * `spender` - The account that is allowed to spend the tokens. + /// + /// # Returns + /// The remaining allowance, or an error if the operation fails. pub fn allowance( id: AssetIdOf, owner: &AccountIdOf, @@ -132,14 +198,35 @@ pub mod pallet { Assets::::allowance(id, owner, spender) } + /// Returns the token name for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The name of the token as a byte vector, or an error if the operation fails. pub fn token_name(id: AssetIdOf) -> Vec { as MetadataInspect>>::name(id) } + /// Returns the token symbol for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The symbol of the token as a byte vector, or an error if the operation fails. pub fn token_symbol(id: AssetIdOf) -> Vec { as MetadataInspect>>::symbol(id) } + /// Returns the token decimals for a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// + /// # Returns + /// The number of decimals of the token as a byte vector, or an error if the operation fails. pub fn token_decimals(id: AssetIdOf) -> u8 { as MetadataInspect>>::decimals(id) } From c10a8d438687ce7e708906ef4381fefdc8bd4f7b Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 22 Jul 2024 23:33:37 +0200 Subject: [PATCH 10/56] fix: integration test after removal fungibles feature primitives --- pallets/api/src/fungibles/tests.rs | 8 +++-- pop-api/Cargo.toml | 2 +- .../integration-tests/src/local_fungibles.rs | 36 +++++++++---------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 185e363b..3f464ee8 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -27,12 +27,15 @@ fn approve_works() { assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); - // Sets a new allowance amount that is lower than the current allowance. + // Approves an amount to spend that is lower than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount / 2)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount / 2); - // Sets a new allowance amount that is higher than the current allowance. + // Approves an amount to spend that is higher than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + // Sets allowance to zero. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, 0)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), 0); }); } @@ -44,6 +47,7 @@ fn increase_allowance_works() { assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + // Additive. assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); }); diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index 2a4784e0..ff9b003a 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -25,4 +25,4 @@ std = [ "primitives/std", "sp-io/std", ] -fungibles = ["primitives/fungibles"] +fungibles = [] diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index aee1fa29..3ff0bf52 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -373,20 +373,18 @@ fn approve_works() { let _ = env_logger::try_init(); let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![]); let amount: Balance = 100 * UNIT; - let asset = 0; - create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); - assert_eq!(decoded::(approve(addr.clone(), asset, BOB, amount)), ConsumerRemaining); - - let addr = - instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); // Asset does not exist. - let asset = 1; assert_eq!( - decoded::(approve(addr.clone(), asset, BOB, amount)), + decoded::(approve(addr.clone(), 0, BOB, amount)), 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); + + let addr = + instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); // Create asset with Alice as owner and mint `amount` to contract address. - create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); + 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!( @@ -394,7 +392,7 @@ fn approve_works() { Module { index: 52, error: 16 }, ); thaw_asset(ALICE, asset); - // Successful approval. + // Successful approvals: assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); assert!(!approve(addr.clone(), asset, BOB, amount).did_revert(), "Contract reverted!"); assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); @@ -416,8 +414,12 @@ fn increase_allowance_works() { let _ = env_logger::try_init(); let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![]); let amount: Balance = 100 * UNIT; - let asset = 0; - create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); + // Asset does not exist. + assert_eq!( + decoded::(increase_allowance(addr.clone(), 0, BOB, amount)), + 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 @@ -425,14 +427,8 @@ fn increase_allowance_works() { let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); - // Asset does not exist. - let asset = 1; - assert_eq!( - decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - Module { index: 52, error: 3 }, - ); // Create asset with Alice as owner and mint `amount` to contract address. - create_asset_and_mint_to(ALICE, asset, addr.clone(), amount); + 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!( @@ -440,7 +436,7 @@ fn increase_allowance_works() { Module { index: 52, error: 16 }, ); thaw_asset(ALICE, asset); - // Successful approval. + // Successful approvals: assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); assert!( !increase_allowance(addr.clone(), asset, BOB, amount).did_revert(), From a8c4bb535bad68ef3fb797a5d72b0c7528c6ff96 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 23 Jul 2024 14:51:50 +0200 Subject: [PATCH 11/56] chore: benchmark approve --- pallets/api/Cargo.toml | 1 + pallets/api/src/fungibles/benchmarking.rs | 60 +++++++++++++++++++++++ pallets/api/src/fungibles/mod.rs | 13 +++-- 3 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 pallets/api/src/fungibles/benchmarking.rs diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 3f462a55..0965405d 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -33,6 +33,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] std = [ diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs new file mode 100644 index 00000000..7c0ad6ce --- /dev/null +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -0,0 +1,60 @@ +//! Benchmarking setup for pallet-cards + +use super::{AccountIdOf, AssetIdOf, Assets, AssetsInstanceOf, BalanceOf, Call, Config, Pallet}; +use frame_benchmarking::{account, v2::*}; +use frame_support::{ + assert_ok, + traits::{ + fungibles::{ + approvals::{Inspect as ApprovalInspect, Mutate}, + Create, Inspect, + }, + Currency, + }, +}; +use frame_system::RawOrigin; +use sp_runtime::traits::Zero; + +const SEED: u32 = 1; + +#[benchmarks( + where + > as Inspect<::AccountId>>::AssetId: Zero, +)] +mod benchmarks { + use super::*; + + // The worst case scenario is when the allowance is set to a value which is lower than the + // current allowance. + #[benchmark] + fn approve() -> Result<(), BenchmarkError> { + let asset = AssetIdOf::::zero(); + let decreased_value = >::from(50u32); + let min_balance = >::from(1u32); + let owner: AccountIdOf = account("Alice", 0, SEED); + let spender: AccountIdOf = account("Bob", 0, SEED); + let value = >::from(100u32); + T::Currency::make_free_balance_be(&owner, 100u32.into()); + assert_ok!( as Create>>::create( + asset.clone().into(), + owner.clone(), + true, + min_balance + )); + assert_ok!( as Mutate>>::approve( + asset.clone(), + &owner, + &spender, + value + )); + + #[extrinsic_call] + _(RawOrigin::Signed(owner.clone()), asset.clone(), spender.clone(), decreased_value); + + assert_eq!(Assets::::allowance(asset, &owner, &spender), decreased_value); + + Ok(()) + } + + impl_benchmark_test_suite!(Pallet, crate::mock::new_test_ext(), crate::mock::Test); +} diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 221da379..3b096490 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -3,6 +3,8 @@ /// API that adheres to standards in the smart contract space. pub use pallet::*; +#[cfg(feature = "runtime-benchmarks")] +mod benchmarking; #[cfg(test)] mod tests; @@ -26,8 +28,8 @@ pub mod pallet { >>::AssetId; type AssetIdParameterOf = >>::AssetIdParameter; - type Assets = pallet_assets::Pallet>; - type AssetsInstanceOf = ::AssetsInstance; + pub(crate) type Assets = pallet_assets::Pallet>; + pub(crate) type AssetsInstanceOf = ::AssetsInstance; type AssetsWeightInfo = >>::WeightInfo; pub(crate) type BalanceOf = > as Inspect< ::AccountId, @@ -94,7 +96,7 @@ pub mod pallet { /// # Returns /// Returns `Ok(())` if successful, or an error if the approval fails. #[pallet::call_index(2)] - #[pallet::weight(T::DbWeight::get().reads(2) + AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] + #[pallet::weight(T::DbWeight::get().reads(1) + AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] pub fn approve( origin: OriginFor, id: AssetIdOf, @@ -102,6 +104,7 @@ pub mod pallet { mut value: BalanceOf, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin.clone()) + // To have the caller pay some fees. .map_err(|e| e.with_weight(T::DbWeight::get().reads(1)))?; let current_allowance = Assets::::allowance(id.clone(), &who, &spender); let spender = T::Lookup::unlookup(spender); @@ -115,7 +118,7 @@ pub mod pallet { value.saturating_reduce(current_allowance); Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { e.with_weight( - T::DbWeight::get().reads(2) + AssetsWeightInfo::::approve_transfer(), + T::DbWeight::get().reads(1) + AssetsWeightInfo::::approve_transfer(), ) })?; } else { @@ -123,7 +126,7 @@ pub mod pallet { Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()).map_err( |e| { e.with_weight( - T::DbWeight::get().reads(2) + AssetsWeightInfo::::cancel_approval(), + T::DbWeight::get().reads(1) + AssetsWeightInfo::::cancel_approval(), ) }, )?; From 4419bad85e4ba213040448a8325c8195049a4448 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 13:59:00 +0200 Subject: [PATCH 12/56] refactor: pallet --- pallets/api/src/fungibles/benchmarking.rs | 8 +- pallets/api/src/fungibles/mod.rs | 145 ++++++++++------------ 2 files changed, 69 insertions(+), 84 deletions(-) diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs index 7c0ad6ce..438aa9b6 100644 --- a/pallets/api/src/fungibles/benchmarking.rs +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -1,6 +1,6 @@ //! Benchmarking setup for pallet-cards -use super::{AccountIdOf, AssetIdOf, Assets, AssetsInstanceOf, BalanceOf, Call, Config, Pallet}; +use super::{AccountIdOf, AssetIdOf, AssetsInstanceOf, AssetsOf, BalanceOf, Call, Config, Pallet}; use frame_benchmarking::{account, v2::*}; use frame_support::{ assert_ok, @@ -35,13 +35,13 @@ mod benchmarks { let spender: AccountIdOf = account("Bob", 0, SEED); let value = >::from(100u32); T::Currency::make_free_balance_be(&owner, 100u32.into()); - assert_ok!( as Create>>::create( + assert_ok!( as Create>>::create( asset.clone().into(), owner.clone(), true, min_balance )); - assert_ok!( as Mutate>>::approve( + assert_ok!( as Mutate>>::approve( asset.clone(), &owner, &spender, @@ -51,7 +51,7 @@ mod benchmarks { #[extrinsic_call] _(RawOrigin::Signed(owner.clone()), asset.clone(), spender.clone(), decreased_value); - assert_eq!(Assets::::allowance(asset, &owner, &spender), decreased_value); + assert_eq!(AssetsOf::::allowance(asset, &owner, &spender), decreased_value); Ok(()) } diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 3b096490..8e474303 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -1,61 +1,68 @@ /// The fungibles pallet serves as a wrapper around the pallet_assets, offering a streamlined /// interface for interacting with fungible assets. The goal is to provide a simplified, consistent /// API that adheres to standards in the smart contract space. -pub use pallet::*; - #[cfg(feature = "runtime-benchmarks")] mod benchmarking; #[cfg(test)] mod tests; +use frame_support::traits::fungibles::{metadata::Inspect as MetadataInspect, Inspect}; +pub use pallet::*; + +type AccountIdOf = ::AccountId; +type AssetIdOf = > as Inspect< + ::AccountId, +>>::AssetId; +type AssetIdParameterOf = >>::AssetIdParameter; +type AssetsOf = pallet_assets::Pallet>; +type AssetsInstanceOf = ::AssetsInstance; +type AssetsWeightInfoOf = >>::WeightInfo; +type BalanceOf = > as Inspect< + ::AccountId, +>>::Balance; + #[frame_support::pallet] pub mod pallet { + use super::*; use frame_support::{ dispatch::{DispatchResult, DispatchResultWithPostInfo, WithPostDispatchInfo}, pallet_prelude::*, - traits::fungibles::{ - approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, Inspect, - }, + traits::fungibles::approvals::Inspect as ApprovalInspect, }; use frame_system::pallet_prelude::*; use pallet_assets::WeightInfo; use sp_runtime::{traits::StaticLookup, Saturating}; use sp_std::vec::Vec; - pub(crate) type AccountIdOf = ::AccountId; - pub(crate) type AssetIdOf = > as Inspect< - ::AccountId, - >>::AssetId; - type AssetIdParameterOf = - >>::AssetIdParameter; - pub(crate) type Assets = pallet_assets::Pallet>; - pub(crate) type AssetsInstanceOf = ::AssetsInstance; - type AssetsWeightInfo = >>::WeightInfo; - pub(crate) type BalanceOf = > as Inspect< - ::AccountId, - >>::Balance; - - /// The required input for state queries through the fungibles api. + /// State reads for the fungibles api with required input. #[derive(Encode, Decode, Debug, MaxEncodedLen)] #[repr(u8)] #[allow(clippy::unnecessary_cast)] - pub enum FungiblesKey { + pub enum Read { + /// Total token supply for a given asset ID. #[codec(index = 0)] TotalSupply(AssetIdOf), + /// Account balance for a given asset ID. #[codec(index = 1)] BalanceOf(AssetIdOf, AccountIdOf), + /// Allowance for a spender approved by an owner, for a given asset ID. #[codec(index = 2)] Allowance(AssetIdOf, AccountIdOf, AccountIdOf), + /// Token name for a given asset ID. #[codec(index = 3)] TokenName(AssetIdOf), + /// Token symbol for a given asset ID. #[codec(index = 4)] TokenSymbol(AssetIdOf), + /// Token decimals for a given asset ID. #[codec(index = 5)] TokenDecimals(AssetIdOf), } + /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: frame_system::Config + pallet_assets::Config { + /// The instance of pallet assets it is tightly coupled to. type AssetsInstance; } @@ -67,15 +74,12 @@ pub mod pallet { /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional /// `data` in unspecified format. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. /// * `to` - The recipient account. /// * `value` - The number of tokens to transfer. - /// - /// # Returns - /// Returns `Ok(())` if successful, or an error if the transfer fails. #[pallet::call_index(0)] - #[pallet::weight(AssetsWeightInfo::::transfer_keep_alive())] + #[pallet::weight(AssetsWeightInfoOf::::transfer_keep_alive())] pub fn transfer( origin: OriginFor, id: AssetIdOf, @@ -83,70 +87,69 @@ pub mod pallet { amount: BalanceOf, ) -> DispatchResult { let target = T::Lookup::unlookup(target); - Assets::::transfer_keep_alive(origin, id.into(), target, amount) + AssetsOf::::transfer_keep_alive(origin, id.into(), target, amount) } /// Approves an account to spend a specified number of tokens on behalf of the caller. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. /// * `spender` - The account that is allowed to spend the tokens. /// * `value` - The number of tokens to approve. - /// - /// # Returns - /// Returns `Ok(())` if successful, or an error if the approval fails. #[pallet::call_index(2)] - #[pallet::weight(T::DbWeight::get().reads(1) + AssetsWeightInfo::::cancel_approval() + AssetsWeightInfo::::approve_transfer())] + #[pallet::weight(T::DbWeight::get().reads(1) + AssetsWeightInfoOf::::cancel_approval() + AssetsWeightInfoOf::::approve_transfer())] pub fn approve( origin: OriginFor, id: AssetIdOf, spender: AccountIdOf, - mut value: BalanceOf, + value: BalanceOf, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin.clone()) // To have the caller pay some fees. .map_err(|e| e.with_weight(T::DbWeight::get().reads(1)))?; - let current_allowance = Assets::::allowance(id.clone(), &who, &spender); + let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); let spender = T::Lookup::unlookup(spender); let id: AssetIdParameterOf = id.into(); // If the new value is equal to the current allowance, do nothing. if value == current_allowance { return Ok(().into()); } - // If the new value is greater than the current allowance, approve the difference. + // If the new value is greater than the current allowance, approve the difference + // because `approve_transfer` works additively (see pallet-assets). if value > current_allowance { - value.saturating_reduce(current_allowance); - Assets::::approve_transfer(origin, id, spender, value).map_err(|e| { + AssetsOf::::approve_transfer( + origin, + id, + spender, + value.saturating_sub(current_allowance), + ) + .map_err(|e| { e.with_weight( - T::DbWeight::get().reads(1) + AssetsWeightInfo::::approve_transfer(), + T::DbWeight::get().reads(1) + AssetsWeightInfoOf::::approve_transfer(), ) })?; } else { // If the new value is less than the current allowance, cancel the approval and set the new value - Assets::::cancel_approval(origin.clone(), id.clone(), spender.clone()).map_err( - |e| { + AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + .map_err(|e| { e.with_weight( - T::DbWeight::get().reads(1) + AssetsWeightInfo::::cancel_approval(), + T::DbWeight::get().reads(1) + + AssetsWeightInfoOf::::cancel_approval(), ) - }, - )?; - Assets::::approve_transfer(origin, id, spender, value)?; + })?; + AssetsOf::::approve_transfer(origin, id, spender, value)?; } - Ok(().into()) } /// Increases the allowance of a spender. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. /// * `spender` - The account that is allowed to spend the tokens. /// * `value` - The number of tokens to increase the allowance by. - /// - /// # Returns - /// Returns `Ok(())` if successful, or an error if the operation fails. #[pallet::call_index(3)] - #[pallet::weight(AssetsWeightInfo::::approve_transfer())] + #[pallet::weight(AssetsWeightInfoOf::::approve_transfer())] pub fn increase_allowance( origin: OriginFor, id: AssetIdOf, @@ -154,84 +157,66 @@ pub mod pallet { value: BalanceOf, ) -> DispatchResult { let spender = T::Lookup::unlookup(spender); - Assets::::approve_transfer(origin, id.into(), spender, value) + AssetsOf::::approve_transfer(origin, id.into(), spender, value) } } impl Pallet { /// Returns the total token supply for a given asset ID. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. - /// - /// # Returns - /// The total supply of the token, or an error if the operation fails. pub fn total_supply(id: AssetIdOf) -> BalanceOf { - Assets::::total_supply(id) + AssetsOf::::total_supply(id) } /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if /// the account is non-existent. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. /// * `owner` - The account whose balance is being queried. - /// - /// # Returns - /// The balance of the specified account, or an error if the operation fails. pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { - Assets::::balance(id, owner) + AssetsOf::::balance(id, owner) } /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given /// asset ID. Returns `0` if no allowance has been set. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. /// * `owner` - The account that owns the tokens. /// * `spender` - The account that is allowed to spend the tokens. - /// - /// # Returns - /// The remaining allowance, or an error if the operation fails. pub fn allowance( id: AssetIdOf, owner: &AccountIdOf, spender: &AccountIdOf, ) -> BalanceOf { - Assets::::allowance(id, owner, spender) + AssetsOf::::allowance(id, owner, spender) } /// Returns the token name for a given asset ID. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. - /// - /// # Returns - /// The name of the token as a byte vector, or an error if the operation fails. pub fn token_name(id: AssetIdOf) -> Vec { - as MetadataInspect>>::name(id) + as MetadataInspect>>::name(id) } /// Returns the token symbol for a given asset ID. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. - /// - /// # Returns - /// The symbol of the token as a byte vector, or an error if the operation fails. pub fn token_symbol(id: AssetIdOf) -> Vec { - as MetadataInspect>>::symbol(id) + as MetadataInspect>>::symbol(id) } /// Returns the token decimals for a given asset ID. /// - /// # Arguments + /// # Parameters /// * `id` - The ID of the asset. - /// - /// # Returns - /// The number of decimals of the token as a byte vector, or an error if the operation fails. pub fn token_decimals(id: AssetIdOf) -> u8 { - as MetadataInspect>>::decimals(id) + as MetadataInspect>>::decimals(id) } } } From 792fa0754b47d94c91bed3a32deb0a53f4a6ff58 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 13:59:33 +0200 Subject: [PATCH 13/56] refactor: integration-tests --- pop-api/integration-tests/src/local_fungibles.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 3ff0bf52..a8511ffb 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -348,11 +348,11 @@ fn transfer_works() { Module { index: 52, error: 0 }, ); // Successful transfer. - let bob_balance_before_transfer = Assets::balance(asset, &BOB); + let balance_before_transfer = Assets::balance(asset, &BOB); let result = transfer(addr.clone(), asset, BOB, amount / 2); assert!(!result.did_revert(), "Contract reverted!"); - let bob_balance_after_transfer = Assets::balance(asset, &BOB); - assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + amount / 2); + let balance_after_transfer = Assets::balance(asset, &BOB); + assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); // Transfer asset to account that does not exist. assert_eq!( decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), @@ -547,11 +547,11 @@ fn token_metadata_works() { // ); // thaw_asset(addr.clone(), asset); // // Successful mint. -// let bob_balance_before_mint = Assets::balance(asset, &BOB); +// let balance_before_mint = Assets::balance(asset, &BOB); // let result = transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8]); // assert!(!result.did_revert(), "Contract reverted!"); -// let bob_balance_after_mint = Assets::balance(asset, &BOB); -// assert_eq!(bob_balance_after_mint, bob_balance_before_mint + amount); +// let balance_after_mint = Assets::balance(asset, &BOB); +// assert_eq!(balance_after_mint, balance_before_mint + amount); // // Can not mint more tokens than Balance::MAX. // assert_eq!( // decoded::(transfer_from( From 96da2f0727bcf5018316bb7d7d6b51ae7b704960 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 14:11:06 +0200 Subject: [PATCH 14/56] refactor: ink api library --- pallets/api/src/fungibles/mod.rs | 12 ++++++------ pop-api/Cargo.toml | 3 ++- pop-api/src/lib.rs | 2 ++ pop-api/src/v0/assets/fungibles.rs | 16 ++++++++-------- pop-api/src/v0/mod.rs | 1 + 5 files changed, 19 insertions(+), 15 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 8e474303..4ad2e2d4 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -49,13 +49,13 @@ pub mod pallet { #[codec(index = 2)] Allowance(AssetIdOf, AccountIdOf, AccountIdOf), /// Token name for a given asset ID. - #[codec(index = 3)] + #[codec(index = 8)] TokenName(AssetIdOf), /// Token symbol for a given asset ID. - #[codec(index = 4)] + #[codec(index = 9)] TokenSymbol(AssetIdOf), /// Token decimals for a given asset ID. - #[codec(index = 5)] + #[codec(index = 10)] TokenDecimals(AssetIdOf), } @@ -78,7 +78,7 @@ pub mod pallet { /// * `id` - The ID of the asset. /// * `to` - The recipient account. /// * `value` - The number of tokens to transfer. - #[pallet::call_index(0)] + #[pallet::call_index(3)] #[pallet::weight(AssetsWeightInfoOf::::transfer_keep_alive())] pub fn transfer( origin: OriginFor, @@ -96,7 +96,7 @@ pub mod pallet { /// * `id` - The ID of the asset. /// * `spender` - The account that is allowed to spend the tokens. /// * `value` - The number of tokens to approve. - #[pallet::call_index(2)] + #[pallet::call_index(5)] #[pallet::weight(T::DbWeight::get().reads(1) + AssetsWeightInfoOf::::cancel_approval() + AssetsWeightInfoOf::::approve_transfer())] pub fn approve( origin: OriginFor, @@ -148,7 +148,7 @@ pub mod pallet { /// * `id` - The ID of the asset. /// * `spender` - The account that is allowed to spend the tokens. /// * `value` - The number of tokens to increase the allowance by. - #[pallet::call_index(3)] + #[pallet::call_index(6)] #[pallet::weight(AssetsWeightInfoOf::::approve_transfer())] pub fn increase_allowance( origin: OriginFor, diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index ff9b003a..5638b9d2 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -25,4 +25,5 @@ std = [ "primitives/std", "sp-io/std", ] -fungibles = [] +assets = [] +fungibles = ["assets"] diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index a3f75f3f..d2470a92 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -13,6 +13,8 @@ pub type Result = core::result::Result; mod constants { // Errors: pub(crate) const DECODING_FAILED: u32 = 255; + // TODO: Not used but will be used in the future when the remaining fungibles features will be + // implemented. pub(crate) const _MODULE_ERROR: u8 = 3; // Function IDs: diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 0805393f..42373cd7 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -23,23 +23,23 @@ mod constants { /// - allowance pub const ALLOWANCE: u8 = 2; /// - transfer - pub(super) const TRANSFER: u8 = 0; + pub(super) const TRANSFER: u8 = 3; /// - transfer_from - pub(super) const TRANSFER_FROM: u8 = 1; + pub(super) const TRANSFER_FROM: u8 = 4; /// - approve - pub(super) const APPROVE: u8 = 2; + pub(super) const APPROVE: u8 = 5; /// - increase_allowance - pub(super) const INCREASE_ALLOWANCE: u8 = 3; + pub(super) const INCREASE_ALLOWANCE: u8 = 6; /// - decrease_allowance - pub(super) const DECREASE_ALLOWANCE: u8 = 4; + pub(super) const DECREASE_ALLOWANCE: u8 = 7; /// 2. PSP-22 Metadata Interface: /// - token_name - pub const TOKEN_NAME: u8 = 3; + pub const TOKEN_NAME: u8 = 8; /// - token_symbol - pub const TOKEN_SYMBOL: u8 = 4; + pub const TOKEN_SYMBOL: u8 = 9; /// - token_decimals - pub const TOKEN_DECIMALS: u8 = 5; + pub const TOKEN_DECIMALS: u8 = 10; // 3. Asset Management: // - create diff --git a/pop-api/src/v0/mod.rs b/pop-api/src/v0/mod.rs index 4a0da363..1c3642e1 100644 --- a/pop-api/src/v0/mod.rs +++ b/pop-api/src/v0/mod.rs @@ -1,5 +1,6 @@ use crate::{primitives::error::Error, StatusCode}; +#[cfg(feature = "assets")] pub mod assets; pub(crate) const V0: u8 = 0; From cb54a57d9174f23e36fc9395dc81d1e57253b455 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 16:33:46 +0200 Subject: [PATCH 15/56] refactor: devnet runtime --- runtime/devnet/Cargo.toml | 1 + runtime/devnet/src/config/api.rs | 31 ++++++++++++++++++++++++++++ runtime/devnet/src/config/assets.rs | 8 ++----- runtime/devnet/src/config/mod.rs | 1 + runtime/devnet/src/extensions/mod.rs | 18 +++++++++------- runtime/devnet/src/lib.rs | 28 ------------------------- 6 files changed, 46 insertions(+), 41 deletions(-) create mode 100644 runtime/devnet/src/config/api.rs diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 4a31f6f6..113f1fe1 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -171,6 +171,7 @@ runtime-benchmarks = [ "frame-system-benchmarking/runtime-benchmarks", "frame-system/runtime-benchmarks", "pallet-assets/runtime-benchmarks", + "pallet-api/runtime-benchmarks", "pallet-balances/runtime-benchmarks", "pallet-collator-selection/runtime-benchmarks", "pallet-contracts/runtime-benchmarks", diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs new file mode 100644 index 00000000..1a8925bf --- /dev/null +++ b/runtime/devnet/src/config/api.rs @@ -0,0 +1,31 @@ +use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall}; +use codec::{Decode, Encode, MaxEncodedLen}; +use frame_support::traits::Contains; + +#[derive(Encode, Decode, Debug, MaxEncodedLen)] +#[repr(u8)] +pub enum RuntimeStateKeys { + #[codec(index = 150)] + Fungibles(fungibles::Read), +} + +impl fungibles::Config for Runtime { + type AssetsInstance = TrustBackedAssetsInstance; +} + +/// A type to identify allowed calls to the Runtime from contracts. Used by Pop API +pub struct AllowedApiCalls; + +impl Contains for AllowedApiCalls { + fn contains(c: &RuntimeCall) -> bool { + use fungibles::Call as FungiblesCall; + matches!( + c, + RuntimeCall::Fungibles( + FungiblesCall::transfer { .. } + | FungiblesCall::approve { .. } + | FungiblesCall::increase_allowance { .. } + ) + ) + } +} diff --git a/runtime/devnet/src/config/assets.rs b/runtime/devnet/src/config/assets.rs index 26874710..78aed8b5 100644 --- a/runtime/devnet/src/config/assets.rs +++ b/runtime/devnet/src/config/assets.rs @@ -9,8 +9,8 @@ use parachains_common::{AssetIdForTrustBackedAssets, CollectionId, ItemId, Signa use sp_runtime::traits::Verify; use crate::{ - deposit, fungibles, AccountId, Assets, Balance, Balances, BlockNumber, Nfts, Runtime, - RuntimeEvent, RuntimeHoldReason, DAYS, EXISTENTIAL_DEPOSIT, UNIT, + deposit, AccountId, Assets, Balance, Balances, BlockNumber, Nfts, Runtime, RuntimeEvent, + RuntimeHoldReason, DAYS, EXISTENTIAL_DEPOSIT, UNIT, }; /// We allow root to execute privileged asset operations. @@ -120,7 +120,3 @@ impl pallet_assets::Config for Runtime { #[cfg(feature = "runtime-benchmarks")] type BenchmarkHelper = (); } - -impl fungibles::Config for Runtime { - type AssetsInstance = TrustBackedAssetsInstance; -} diff --git a/runtime/devnet/src/config/mod.rs b/runtime/devnet/src/config/mod.rs index e0aaa3a1..f62ffa76 100644 --- a/runtime/devnet/src/config/mod.rs +++ b/runtime/devnet/src/config/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod api; pub mod assets; mod contracts; mod proxy; diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 4496c458..27af328a 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -1,13 +1,15 @@ mod v0; use crate::{ - config::assets::TrustBackedAssetsInstance, + config::{ + api::{AllowedApiCalls, RuntimeStateKeys}, + assets::TrustBackedAssetsInstance, + }, fungibles::{ self, - FungiblesKey::{self, *}, + Read::{self, *}, }, - state_keys::RuntimeStateKeys, - AccountId, AllowedApiCalls, RuntimeCall, RuntimeOrigin, + AccountId, RuntimeCall, RuntimeOrigin, }; use codec::{Decode, Encode}; use frame_support::{ @@ -187,16 +189,18 @@ where env.write(&result, false, None) } -// Example wrapper to enable versioning of `RuntimeStateKeys`. +/// Wrapper to enable versioning of `RuntimeStateKeys`. #[derive(Decode, Debug)] enum VersionedStateRead { + /// Version zero of reading state from api. #[codec(index = 0)] V0(RuntimeStateKeys), } -// Wrapper to enable versioning of `RuntimeCall`. +/// Wrapper to enable versioning of `RuntimeCall`. #[derive(Decode, Debug)] enum VersionedDispatch { + /// Version zero of dispatch calls from api. #[codec(index = 0)] V0(RuntimeCall), } @@ -282,7 +286,7 @@ impl TryFrom for FuncId { } fn read_fungibles_state( - key: FungiblesKey, + key: Read, env: &mut Environment, ) -> Result, DispatchError> where diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 4d4ffc7c..15971e74 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -252,22 +252,6 @@ impl Contains for FilteredCalls { } } -/// A type to identify allowed calls to the Runtime from contracts. Used by Pop API -pub struct AllowedApiCalls; -impl Contains for AllowedApiCalls { - fn contains(c: &RuntimeCall) -> bool { - use fungibles::Call as FungiblesCall; - matches!( - c, - RuntimeCall::Fungibles( - FungiblesCall::transfer { .. } - | FungiblesCall::approve { .. } - | FungiblesCall::increase_allowance { .. } - ) - ) - } -} - /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from /// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), /// but overridden as needed. @@ -975,15 +959,3 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } - -pub(crate) mod state_keys { - use super::fungibles; - use codec::{Decode, Encode, MaxEncodedLen}; - - #[derive(Encode, Decode, Debug, MaxEncodedLen)] - #[repr(u8)] - pub enum RuntimeStateKeys { - #[codec(index = 150)] - Fungibles(fungibles::FungiblesKey), - } -} From 9ec274490e62269547abd41571aafd08ba1fa9ed Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 18:26:06 +0200 Subject: [PATCH 16/56] chore: benchmark fungibles approve --- node/src/command.rs | 13 +-- pallets/api/src/fungibles/benchmarking.rs | 40 +++++-- pallets/api/src/fungibles/mod.rs | 8 +- pallets/api/src/fungibles/weights.rs | 75 +++++++++++++ pallets/api/src/mock.rs | 1 + runtime/devnet/src/config/api.rs | 1 + runtime/devnet/src/lib.rs | 1 + scripts/pallet-weights-template.hbs | 122 ++++++++++++++++++++++ 8 files changed, 241 insertions(+), 20 deletions(-) create mode 100644 pallets/api/src/fungibles/weights.rs create mode 100644 scripts/pallet-weights-template.hbs diff --git a/node/src/command.rs b/node/src/command.rs index 98af5450..8ba956e1 100644 --- a/node/src/command.rs +++ b/node/src/command.rs @@ -64,18 +64,9 @@ impl RuntimeResolver for PathBuf { fn load_spec(id: &str) -> std::result::Result, String> { Ok(match id { - #[cfg(not(feature = "paseo"))] - "dev-rococo" => Box::new(chain_spec::development_config(Relay::RococoLocal)), - #[cfg(feature = "paseo")] - "dev-paseo" => Box::new(chain_spec::development_config(Relay::PaseoLocal)), - #[cfg(not(feature = "paseo"))] - "pop-rococo" => Box::new(chain_spec::testnet_config(Relay::Rococo)), - #[cfg(feature = "paseo")] - "pop-paseo" => Box::new(chain_spec::testnet_config(Relay::Paseo)), - #[cfg(feature = "paseo")] + "dev" | "dev-paseo" => Box::new(chain_spec::development_config(Relay::PaseoLocal)), + "test" | "pop-paseo" => Box::new(chain_spec::testnet_config(Relay::Paseo)), "" | "local" => Box::new(chain_spec::development_config(Relay::PaseoLocal)), - #[cfg(not(feature = "paseo"))] - "" | "local" => Box::new(chain_spec::development_config(Relay::RococoLocal)), path => { let path: PathBuf = path.into(); match path.runtime() { diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs index 438aa9b6..6f182932 100644 --- a/pallets/api/src/fungibles/benchmarking.rs +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -1,4 +1,4 @@ -//! Benchmarking setup for pallet-cards +//! Benchmarking setup for pallet-api::fungibles use super::{AccountIdOf, AssetIdOf, AssetsInstanceOf, AssetsOf, BalanceOf, Call, Config, Pallet}; use frame_benchmarking::{account, v2::*}; @@ -17,6 +17,13 @@ use sp_runtime::traits::Zero; const SEED: u32 = 1; +// See if `generic_event` has been emitted. +fn assert_has_event( + generic_event: >>::RuntimeEvent, +) { + frame_system::Pallet::::assert_has_event(generic_event.into()); +} + #[benchmarks( where > as Inspect<::AccountId>>::AssetId: Zero, @@ -28,30 +35,49 @@ mod benchmarks { // current allowance. #[benchmark] fn approve() -> Result<(), BenchmarkError> { - let asset = AssetIdOf::::zero(); + let asset_id = AssetIdOf::::zero(); let decreased_value = >::from(50u32); let min_balance = >::from(1u32); let owner: AccountIdOf = account("Alice", 0, SEED); let spender: AccountIdOf = account("Bob", 0, SEED); let value = >::from(100u32); - T::Currency::make_free_balance_be(&owner, 100u32.into()); + T::Currency::make_free_balance_be(&owner, u32::MAX.into()); assert_ok!( as Create>>::create( - asset.clone().into(), + asset_id.clone().into(), owner.clone(), true, min_balance )); assert_ok!( as Mutate>>::approve( - asset.clone(), + asset_id.clone(), &owner, &spender, value )); #[extrinsic_call] - _(RawOrigin::Signed(owner.clone()), asset.clone(), spender.clone(), decreased_value); + _(RawOrigin::Signed(owner.clone()), asset_id.clone(), spender.clone(), decreased_value); - assert_eq!(AssetsOf::::allowance(asset, &owner, &spender), decreased_value); + assert_eq!(AssetsOf::::allowance(asset_id.clone(), &owner, &spender), decreased_value); + // To make sure both dispatchables have been successfully executed - i.e. worst case + // scenario. + assert_has_event::( + pallet_assets::Event::ApprovalCancelled { + asset_id: asset_id.clone(), + owner: owner.clone(), + delegate: spender.clone(), + } + .into(), + ); + assert_has_event::( + pallet_assets::Event::ApprovedTransfer { + asset_id, + source: owner, + delegate: spender, + amount: value, + } + .into(), + ); Ok(()) } diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 4ad2e2d4..d80fc310 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -5,9 +5,12 @@ mod benchmarking; #[cfg(test)] mod tests; +pub mod weights; use frame_support::traits::fungibles::{metadata::Inspect as MetadataInspect, Inspect}; pub use pallet::*; +use pallet_assets::WeightInfo as AssetsWeightInfoTrait; +use weights::WeightInfo; type AccountIdOf = ::AccountId; type AssetIdOf = > as Inspect< @@ -30,7 +33,6 @@ pub mod pallet { traits::fungibles::approvals::Inspect as ApprovalInspect, }; use frame_system::pallet_prelude::*; - use pallet_assets::WeightInfo; use sp_runtime::{traits::StaticLookup, Saturating}; use sp_std::vec::Vec; @@ -64,6 +66,8 @@ pub mod pallet { pub trait Config: frame_system::Config + pallet_assets::Config { /// The instance of pallet assets it is tightly coupled to. type AssetsInstance; + /// Weight information for extrinsics in this pallet. + type WeightInfo: WeightInfo; } #[pallet::pallet] @@ -97,7 +101,7 @@ pub mod pallet { /// * `spender` - The account that is allowed to spend the tokens. /// * `value` - The number of tokens to approve. #[pallet::call_index(5)] - #[pallet::weight(T::DbWeight::get().reads(1) + AssetsWeightInfoOf::::cancel_approval() + AssetsWeightInfoOf::::approve_transfer())] + #[pallet::weight(::WeightInfo::approve())] pub fn approve( origin: OriginFor, id: AssetIdOf, diff --git a/pallets/api/src/fungibles/weights.rs b/pallets/api/src/fungibles/weights.rs new file mode 100644 index 00000000..ed2b04fc --- /dev/null +++ b/pallets/api/src/fungibles/weights.rs @@ -0,0 +1,75 @@ +//! Autogenerated weights for `pallet_api::fungibles` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 33.0.0 +//! DATE: 2024-07-24, STEPS: `20`, REPEAT: `5`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `R0GUE`, CPU: `` +//! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` + +// Executed Command: +// ./target/release/pop-node +// benchmark +// pallet +// --chain=dev +// --wasm-execution=compiled +// --pallet=pallet_api::fungibles +// --steps=20 +// --repeat=5 +// --json +// --template +// ./scripts/pallet-weights-template.hbs +// --output=./pallets/api/src/fungibles/weights.rs +// --extrinsic= + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `pallet_api::fungibles`. +pub trait WeightInfo { + fn approve() -> Weight; +} + +/// Weights for `pallet_api::fungibles` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +impl WeightInfo for SubstrateWeight { + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn approve() -> Weight { + // Proof Size summary in bytes: + // Measured: `515` + // Estimated: `3675` + // Minimum execution time: 53_000_000 picoseconds. + Weight::from_parts(54_000_000, 3675) + .saturating_add(T::DbWeight::get().reads(3_u64)) + .saturating_add(T::DbWeight::get().writes(3_u64)) + } +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + /// Storage: `Assets::Approvals` (r:1 w:1) + /// Proof: `Assets::Approvals` (`max_values`: None, `max_size`: Some(148), added: 2623, mode: `MaxEncodedLen`) + /// Storage: `Assets::Asset` (r:1 w:1) + /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) + /// Storage: `System::Account` (r:1 w:1) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) + fn approve() -> Weight { + // Proof Size summary in bytes: + // Measured: `515` + // Estimated: `3675` + // Minimum execution time: 53_000_000 picoseconds. + Weight::from_parts(54_000_000, 3675) + .saturating_add(RocksDbWeight::get().reads(3_u64)) + .saturating_add(RocksDbWeight::get().writes(3_u64)) + } +} + diff --git a/pallets/api/src/mock.rs b/pallets/api/src/mock.rs index 5f0340a9..f5d155ef 100644 --- a/pallets/api/src/mock.rs +++ b/pallets/api/src/mock.rs @@ -98,6 +98,7 @@ impl pallet_assets::Config for Test { } impl crate::fungibles::Config for Test { type AssetsInstance = AssetsInstance; + type WeightInfo = (); } pub(crate) const ALICE: AccountId = 1; diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 1a8925bf..cbf7401b 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -11,6 +11,7 @@ pub enum RuntimeStateKeys { impl fungibles::Config for Runtime { type AssetsInstance = TrustBackedAssetsInstance; + type WeightInfo = fungibles::weights::SubstrateWeight; } /// A type to identify allowed calls to the Runtime from contracts. Used by Pop API diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 15971e74..e22201f1 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -599,6 +599,7 @@ construct_runtime!( mod benches { frame_benchmarking::define_benchmarks!( [frame_system, SystemBench::] + [pallet_api::fungibles, Fungibles] [pallet_balances, Balances] [pallet_session, SessionBench::] [pallet_timestamp, Timestamp] diff --git a/scripts/pallet-weights-template.hbs b/scripts/pallet-weights-template.hbs new file mode 100644 index 00000000..9e1e5a46 --- /dev/null +++ b/scripts/pallet-weights-template.hbs @@ -0,0 +1,122 @@ +{{header}} +//! Autogenerated weights for `{{pallet}}` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION {{version}} +//! DATE: {{date}}, STEPS: `{{cmd.steps}}`, REPEAT: `{{cmd.repeat}}`, LOW RANGE: `{{cmd.lowest_range_values}}`, HIGH RANGE: `{{cmd.highest_range_values}}` +//! WORST CASE MAP SIZE: `{{cmd.worst_case_map_values}}` +//! HOSTNAME: `R0GUE`, CPU: `{{cpuname}}` +//! WASM-EXECUTION: `{{cmd.wasm_execution}}`, CHAIN: `{{cmd.chain}}`, DB CACHE: `{{cmd.db_cache}}` + +// Executed Command: +{{#each args as |arg|}} +// {{arg}} +{{/each}} + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::{Weight, constants::RocksDbWeight}}; +use core::marker::PhantomData; + +/// Weight functions needed for `{{pallet}}`. +pub trait WeightInfo { + {{#each benchmarks as |benchmark|}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{c.name}}: u32, {{/each~}} + ) -> Weight; + {{/each}} +} + +/// Weights for `{{pallet}}` using the Substrate node and recommended hardware. +pub struct SubstrateWeight(PhantomData); +{{#if (eq pallet "frame_system")}} +impl WeightInfo for SubstrateWeight { +{{else}} +impl WeightInfo for SubstrateWeight { +{{/if}} + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(T::DbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(T::DbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(T::DbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(T::DbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} + +// For backwards compatibility and tests. +impl WeightInfo for () { + {{#each benchmarks as |benchmark|}} + {{#each benchmark.comments as |comment|}} + /// {{comment}} + {{/each}} + {{#each benchmark.component_ranges as |range|}} + /// The range of component `{{range.name}}` is `[{{range.min}}, {{range.max}}]`. + {{/each}} + fn {{benchmark.name~}} + ( + {{~#each benchmark.components as |c| ~}} + {{~#if (not c.is_used)}}_{{/if}}{{c.name}}: u32, {{/each~}} + ) -> Weight { + // Proof Size summary in bytes: + // Measured: `{{benchmark.base_recorded_proof_size}}{{#each benchmark.component_recorded_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Estimated: `{{benchmark.base_calculated_proof_size}}{{#each benchmark.component_calculated_proof_size as |cp|}} + {{cp.name}} * ({{cp.slope}} ±{{underscore cp.error}}){{/each}}` + // Minimum execution time: {{underscore benchmark.min_execution_time}}_000 picoseconds. + Weight::from_parts({{underscore benchmark.base_weight}}, {{benchmark.base_calculated_proof_size}}) + {{#each benchmark.component_weight as |cw|}} + // Standard Error: {{underscore cw.error}} + .saturating_add(Weight::from_parts({{underscore cw.slope}}, 0).saturating_mul({{cw.name}}.into())) + {{/each}} + {{#if (ne benchmark.base_reads "0")}} + .saturating_add(RocksDbWeight::get().reads({{benchmark.base_reads}}_u64)) + {{/if}} + {{#each benchmark.component_reads as |cr|}} + .saturating_add(RocksDbWeight::get().reads(({{cr.slope}}_u64).saturating_mul({{cr.name}}.into()))) + {{/each}} + {{#if (ne benchmark.base_writes "0")}} + .saturating_add(RocksDbWeight::get().writes({{benchmark.base_writes}}_u64)) + {{/if}} + {{#each benchmark.component_writes as |cw|}} + .saturating_add(RocksDbWeight::get().writes(({{cw.slope}}_u64).saturating_mul({{cw.name}}.into()))) + {{/each}} + {{#each benchmark.component_calculated_proof_size as |cp|}} + .saturating_add(Weight::from_parts(0, {{cp.slope}}).saturating_mul({{cp.name}}.into())) + {{/each}} + } + {{/each}} +} + From f0fff9777950f32ac4fe2a77e526ddefecda565a Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 18:26:39 +0200 Subject: [PATCH 17/56] test: ensure Lookup configuration --- runtime/devnet/src/lib.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index e22201f1..51b42892 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -960,3 +960,20 @@ cumulus_pallet_parachain_system::register_validate_block! { Runtime = Runtime, BlockExecutor = cumulus_pallet_aura_ext::BlockExecutor::, } + +#[cfg(test)] +mod tests { + use crate::Runtime; + use std::any::TypeId; + + #[test] + fn test_lookup_config() { + type ExpectedLookup = sp_runtime::traits::AccountIdLookup; + type ConfigLookup = ::Lookup; + + let expected_type_id = TypeId::of::(); + let config_type_id = TypeId::of::(); + + assert_eq!(config_type_id, expected_type_id); + } +} From 4a476b01790f06c21d111fbc7fd697078c2cd49e Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 24 Jul 2024 18:26:06 +0200 Subject: [PATCH 18/56] fix: approve benchmark + weights --- pallets/api/src/fungibles/benchmarking.rs | 78 ++++++++++++++--------- pallets/api/src/fungibles/mod.rs | 35 +++++----- pallets/api/src/fungibles/weights.rs | 51 ++++++++++----- 3 files changed, 101 insertions(+), 63 deletions(-) diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs index 6f182932..bb225f20 100644 --- a/pallets/api/src/fungibles/benchmarking.rs +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -31,19 +31,20 @@ fn assert_has_event( mod benchmarks { use super::*; - // The worst case scenario is when the allowance is set to a value which is lower than the - // current allowance. + // Parameter: + // - 'a': whether `approve_transfer` has been called. + // - 'c': whether `cancel_approval` has been called. #[benchmark] - fn approve() -> Result<(), BenchmarkError> { + fn approve(a: Linear<0, 1>, c: Linear<0, 1>) -> Result<(), BenchmarkError> { let asset_id = AssetIdOf::::zero(); - let decreased_value = >::from(50u32); let min_balance = >::from(1u32); let owner: AccountIdOf = account("Alice", 0, SEED); let spender: AccountIdOf = account("Bob", 0, SEED); - let value = >::from(100u32); + let current_allowance = >::from(u32::MAX / 2); T::Currency::make_free_balance_be(&owner, u32::MAX.into()); + // Set the `current_allowance`. assert_ok!( as Create>>::create( - asset_id.clone().into(), + asset_id.clone(), owner.clone(), true, min_balance @@ -52,33 +53,52 @@ mod benchmarks { asset_id.clone(), &owner, &spender, - value + current_allowance, )); + let approval_value = match (a, c) { + // Equal to the current allowance. + (0, 0) => current_allowance, + // Greater than the current allowance. + (1, 0) => >::from(u32::MAX), + // Zero. + (0, 1) => >::from(0u32), + // Smaller than the current allowance. + (1, 1) => >::from(u32::MAX / 4), + _ => unreachable!("values can only be 0 or 1"), + }; #[extrinsic_call] - _(RawOrigin::Signed(owner.clone()), asset_id.clone(), spender.clone(), decreased_value); - - assert_eq!(AssetsOf::::allowance(asset_id.clone(), &owner, &spender), decreased_value); - // To make sure both dispatchables have been successfully executed - i.e. worst case - // scenario. - assert_has_event::( - pallet_assets::Event::ApprovalCancelled { - asset_id: asset_id.clone(), - owner: owner.clone(), - delegate: spender.clone(), - } - .into(), - ); - assert_has_event::( - pallet_assets::Event::ApprovedTransfer { - asset_id, - source: owner, - delegate: spender, - amount: value, - } - .into(), - ); + _(RawOrigin::Signed(owner.clone()), asset_id.clone(), spender.clone(), approval_value); + assert_eq!(AssetsOf::::allowance(asset_id.clone(), &owner, &spender), approval_value); + if c == 1 { + assert_has_event::( + pallet_assets::Event::ApprovalCancelled { + asset_id: asset_id.clone(), + owner: owner.clone(), + delegate: spender.clone(), + } + .into(), + ); + } + if a == 1 { + let amount = match c { + // When the allowance was cancelled and then approved with the new value. + 1 => approval_value, + // When the allowance was increased. + 0 => approval_value - current_allowance, + _ => unreachable!("`c` can only be 0 or 1"), + }; + assert_has_event::( + pallet_assets::Event::ApprovedTransfer { + asset_id, + source: owner, + delegate: spender, + amount, + } + .into(), + ); + } Ok(()) } diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index d80fc310..3067e1d3 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -33,7 +33,10 @@ pub mod pallet { traits::fungibles::approvals::Inspect as ApprovalInspect, }; use frame_system::pallet_prelude::*; - use sp_runtime::{traits::StaticLookup, Saturating}; + use sp_runtime::{ + traits::{StaticLookup, Zero}, + Saturating, + }; use sp_std::vec::Vec; /// State reads for the fungibles api with required input. @@ -101,22 +104,23 @@ pub mod pallet { /// * `spender` - The account that is allowed to spend the tokens. /// * `value` - The number of tokens to approve. #[pallet::call_index(5)] - #[pallet::weight(::WeightInfo::approve())] + #[pallet::weight(::WeightInfo::approve(1, 1))] pub fn approve( origin: OriginFor, id: AssetIdOf, spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin.clone()) - // To have the caller pay some fees. - .map_err(|e| e.with_weight(T::DbWeight::get().reads(1)))?; + let weight = |approve: u32, cancel: u32| -> Weight { + ::WeightInfo::approve(cancel, approve) + }; + let who = ensure_signed(origin.clone()).map_err(|e| e.with_weight(weight(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); let spender = T::Lookup::unlookup(spender); let id: AssetIdParameterOf = id.into(); // If the new value is equal to the current allowance, do nothing. if value == current_allowance { - return Ok(().into()); + return Ok(Some(weight(0, 0)).into()); } // If the new value is greater than the current allowance, approve the difference // because `approve_transfer` works additively (see pallet-assets). @@ -127,23 +131,18 @@ pub mod pallet { spender, value.saturating_sub(current_allowance), ) - .map_err(|e| { - e.with_weight( - T::DbWeight::get().reads(1) + AssetsWeightInfoOf::::approve_transfer(), - ) - })?; + .map_err(|e| e.with_weight(weight(1, 0)))?; + Ok(Some(weight(1, 0)).into()) } else { // If the new value is less than the current allowance, cancel the approval and set the new value AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) - .map_err(|e| { - e.with_weight( - T::DbWeight::get().reads(1) - + AssetsWeightInfoOf::::cancel_approval(), - ) - })?; + .map_err(|e| e.with_weight(weight(0, 1)))?; + if value.is_zero() { + return Ok(Some(weight(0, 1)).into()); + } AssetsOf::::approve_transfer(origin, id, spender, value)?; + Ok(().into()) } - Ok(().into()) } /// Increases the allowance of a spender. diff --git a/pallets/api/src/fungibles/weights.rs b/pallets/api/src/fungibles/weights.rs index ed2b04fc..a6c31654 100644 --- a/pallets/api/src/fungibles/weights.rs +++ b/pallets/api/src/fungibles/weights.rs @@ -1,7 +1,8 @@ + //! Autogenerated weights for `pallet_api::fungibles` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 33.0.0 -//! DATE: 2024-07-24, STEPS: `20`, REPEAT: `5`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2024-07-25, STEPS: `20`, REPEAT: `5`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` //! HOSTNAME: `R0GUE`, CPU: `` //! WASM-EXECUTION: `Compiled`, CHAIN: `Some("dev")`, DB CACHE: `1024` @@ -31,7 +32,7 @@ use core::marker::PhantomData; /// Weight functions needed for `pallet_api::fungibles`. pub trait WeightInfo { - fn approve() -> Weight; + fn approve(a: u32, c: u32, ) -> Weight; } /// Weights for `pallet_api::fungibles` using the Substrate node and recommended hardware. @@ -43,14 +44,23 @@ impl WeightInfo for SubstrateWeight { /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn approve() -> Weight { + /// The range of component `a` is `[0, 1]`. + /// The range of component `c` is `[0, 1]`. + fn approve(a: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `515` - // Estimated: `3675` - // Minimum execution time: 53_000_000 picoseconds. - Weight::from_parts(54_000_000, 3675) - .saturating_add(T::DbWeight::get().reads(3_u64)) - .saturating_add(T::DbWeight::get().writes(3_u64)) + // Measured: `413 + c * (102 ±0)` + // Estimated: `3675 + c * (1797 ±0)` + // Minimum execution time: 35_000_000 picoseconds. + Weight::from_parts(1_207_482, 3675) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(34_649_659, 0).saturating_mul(a.into())) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(56_976_190, 0).saturating_mul(c.into())) + .saturating_add(T::DbWeight::get().reads(2_u64)) + .saturating_add(T::DbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(T::DbWeight::get().writes(2_u64)) + .saturating_add(T::DbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 1797).saturating_mul(c.into())) } } @@ -62,14 +72,23 @@ impl WeightInfo for () { /// Proof: `Assets::Asset` (`max_values`: None, `max_size`: Some(210), added: 2685, mode: `MaxEncodedLen`) /// Storage: `System::Account` (r:1 w:1) /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `MaxEncodedLen`) - fn approve() -> Weight { + /// The range of component `a` is `[0, 1]`. + /// The range of component `c` is `[0, 1]`. + fn approve(a: u32, c: u32, ) -> Weight { // Proof Size summary in bytes: - // Measured: `515` - // Estimated: `3675` - // Minimum execution time: 53_000_000 picoseconds. - Weight::from_parts(54_000_000, 3675) - .saturating_add(RocksDbWeight::get().reads(3_u64)) - .saturating_add(RocksDbWeight::get().writes(3_u64)) + // Measured: `413 + c * (102 ±0)` + // Estimated: `3675 + c * (1797 ±0)` + // Minimum execution time: 35_000_000 picoseconds. + Weight::from_parts(1_207_482, 3675) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(34_649_659, 0).saturating_mul(a.into())) + // Standard Error: 948_955 + .saturating_add(Weight::from_parts(56_976_190, 0).saturating_mul(c.into())) + .saturating_add(RocksDbWeight::get().reads(2_u64)) + .saturating_add(RocksDbWeight::get().reads((1_u64).saturating_mul(c.into()))) + .saturating_add(RocksDbWeight::get().writes(2_u64)) + .saturating_add(RocksDbWeight::get().writes((1_u64).saturating_mul(c.into()))) + .saturating_add(Weight::from_parts(0, 1797).saturating_mul(c.into())) } } From f3cc83a056ef5673c054c3a93552296762b335c7 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Thu, 25 Jul 2024 14:37:50 +0200 Subject: [PATCH 19/56] refactor: primitives --- Cargo.lock | 21 +++++++++---------- Cargo.toml | 2 +- pallets/api/Cargo.toml | 2 -- pop-api/Cargo.toml | 4 ++-- pop-api/integration-tests/Cargo.toml | 4 ++-- .../integration-tests/src/local_fungibles.rs | 2 +- pop-api/src/primitives.rs | 2 +- primitives/Cargo.toml | 3 ++- primitives/README.md | 1 + runtime/devnet/Cargo.toml | 4 ++-- runtime/devnet/src/extensions/mod.rs | 4 ++-- runtime/testnet/Cargo.toml | 4 ++-- 12 files changed, 26 insertions(+), 27 deletions(-) create mode 100644 primitives/README.md diff --git a/Cargo.lock b/Cargo.lock index c0dfbaac..c8f0a6c9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6614,7 +6614,6 @@ dependencies = [ "pallet-assets", "pallet-balances", "parity-scale-codec", - "primitives", "scale-info", "sp-core", "sp-io", @@ -9699,6 +9698,14 @@ dependencies = [ "substrate-prometheus-endpoint", ] +[[package]] +name = "pop-primitives" +version = "0.0.0" +dependencies = [ + "parity-scale-codec", + "scale-info", +] + [[package]] name = "pop-runtime-common" version = "0.0.0" @@ -9762,8 +9769,8 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-runtime-common", + "pop-primitives", "pop-runtime-common", - "primitives", "rand", "scale-info", "smallvec", @@ -9836,8 +9843,8 @@ dependencies = [ "parity-scale-codec", "polkadot-parachain-primitives", "polkadot-runtime-common", + "pop-primitives", "pop-runtime-common", - "primitives", "scale-info", "smallvec", "sp-api", @@ -9943,14 +9950,6 @@ dependencies = [ "uint", ] -[[package]] -name = "primitives" -version = "0.0.0" -dependencies = [ - "parity-scale-codec", - "scale-info", -] - [[package]] name = "prioritized-metered-channel" version = "0.6.1" diff --git a/Cargo.toml b/Cargo.toml index 79bf5ebe..8b2d0a8f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -56,7 +56,7 @@ pallet-api = { path = "pallets/api", default-features = false } pop-runtime-devnet = { path = "runtime/devnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-testnet = { path = "runtime/testnet", default-features = true } # default-features=true required for `-p pop-node` builds pop-runtime-common = { path = "runtime/common", default-features = false } -primitives = { path = "./primitives", default-features = false } +pop-primitives = { path = "./primitives", default-features = false } # Substrate sc-basic-authorship = "0.35.0" diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 0965405d..a813a09c 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -18,7 +18,6 @@ frame-benchmarking.workspace = true frame-support.workspace = true frame-system.workspace = true pallet-assets.workspace = true -primitives.workspace = true sp-runtime.workspace = true sp-std.workspace = true @@ -43,7 +42,6 @@ std = [ "frame-system/std", "pallet-assets/std", "pallet-balances/std", - "primitives/std", "scale-info/std", "sp-core/std", "sp-io/std", diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index 5638b9d2..0f878b4b 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -10,7 +10,7 @@ enumflags2 = { version = "0.7.7" } ink = { version = "5.0.0", default-features = false } sp-io = { version = "31.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } -primitives = { path = "../primitives", default-features = false } +pop-primitives = { path = "../primitives", default-features = false } [lib] name = "pop_api" @@ -22,7 +22,7 @@ default = ["std"] std = [ "enumflags2/std", "ink/std", - "primitives/std", + "pop-primitives/std", "sp-io/std", ] assets = [] diff --git a/pop-api/integration-tests/Cargo.toml b/pop-api/integration-tests/Cargo.toml index 52b3b634..cc56630a 100644 --- a/pop-api/integration-tests/Cargo.toml +++ b/pop-api/integration-tests/Cargo.toml @@ -11,7 +11,7 @@ frame-system = { version = "29.0.0", default-features = false } pallet-balances = { version = "29.0.2", default-features = false } pallet-assets = { version = "30.0.0", default-features = false } pallet-contracts = { version = "28.0.0", default-features = false } -primitives = { path = "../../primitives", default-features = false } +pop-primitives = { path = "../../primitives", default-features = false } pop-runtime-devnet = { path = "../../runtime/devnet", default-features = false } sp-io = { version = "31.0.0", default-features = false } sp-runtime = { version = "32.0.0", default-features = false } @@ -25,7 +25,7 @@ std = [ "pallet-balances/std", "pallet-assets/std", "pallet-contracts/std", - "primitives/std", + "pop-primitives/std", "pop-runtime-devnet/std", "scale/std", "sp-io/std", diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index a8511ffb..7571a0ec 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -1,5 +1,5 @@ use super::*; -use primitives::error::{ +use pop_primitives::error::{ ArithmeticError::*, Error::{self, *}, TokenError::*, diff --git a/pop-api/src/primitives.rs b/pop-api/src/primitives.rs index 34d7c38d..a3d596a5 100644 --- a/pop-api/src/primitives.rs +++ b/pop-api/src/primitives.rs @@ -1,5 +1,5 @@ use ink::env::{DefaultEnvironment, Environment}; -pub use primitives::*; +pub use pop_primitives::*; pub(crate) type AccountId = ::AccountId; pub(crate) type Balance = ::Balance; diff --git a/primitives/Cargo.toml b/primitives/Cargo.toml index cb62ddc0..e7d55ffe 100644 --- a/primitives/Cargo.toml +++ b/primitives/Cargo.toml @@ -1,5 +1,6 @@ [package] -name = "primitives" +name = "pop-primitives" +description = "Primitives crate for Pop" license = "GPL-3.0-only" version = "0.0.0" edition = "2021" diff --git a/primitives/README.md b/primitives/README.md new file mode 100644 index 00000000..ded7918a --- /dev/null +++ b/primitives/README.md @@ -0,0 +1 @@ +Reserved crate for pop-primitives. \ No newline at end of file diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 113f1fe1..46d23284 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -22,7 +22,7 @@ scale-info.workspace = true smallvec.workspace = true # Local -primitives.workspace = true +pop-primitives.workspace = true pop-runtime-common.workspace = true pallet-api.workspace = true @@ -140,7 +140,7 @@ std = [ "parachains-common/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", - "primitives/std", + "pop-primitives/std", "scale-info/std", "sp-api/std", "sp-io/std", diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 27af328a..4b1c1064 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -20,7 +20,7 @@ use frame_support::{ use pallet_contracts::chain_extension::{ BufInBufOutState, ChainExtension, Environment, Ext, InitState, RetVal, }; -use primitives::AssetId; +use pop_primitives::AssetId; use sp_core::crypto::UncheckedFrom; use sp_runtime::{traits::Dispatchable, DispatchError}; use sp_std::vec::Vec; @@ -208,7 +208,7 @@ enum VersionedDispatch { // Converts a `DispatchError` to a `u32` status code based on the version of the API the contract uses. // The contract calling the chain extension can convert the status code to the descriptive `Error`. // -// For `Error` see `primitives::::error::Error`. +// For `Error` see `pop_primitives::::error::Error`. // // The error encoding can vary per version, allowing for flexible and backward-compatible error handling. // As a result, contracts maintain compatibility across different versions of the runtime. diff --git a/runtime/testnet/Cargo.toml b/runtime/testnet/Cargo.toml index f269bed8..79ea9cac 100644 --- a/runtime/testnet/Cargo.toml +++ b/runtime/testnet/Cargo.toml @@ -22,7 +22,7 @@ scale-info.workspace = true smallvec.workspace = true # Local -primitives.workspace = true +pop-primitives.workspace = true pop-runtime-common.workspace = true # Substrate @@ -137,7 +137,7 @@ std = [ "parachains-common/std", "polkadot-parachain-primitives/std", "polkadot-runtime-common/std", - "primitives/std", + "pop-primitives/std", "scale-info/std", "sp-api/std", "sp-io/std", From bd8548976a082b60fd1f500894159fcb218094cc Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Thu, 25 Jul 2024 14:58:51 +0200 Subject: [PATCH 20/56] refactor: bits and pieces --- pallets/api/src/fungibles/tests.rs | 6 ++-- .../integration-tests/src/local_fungibles.rs | 33 +++++++++---------- pop-api/src/lib.rs | 4 +-- 3 files changed, 21 insertions(+), 22 deletions(-) diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 3f464ee8..e42283d1 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -11,10 +11,10 @@ fn transfer_works() { new_test_ext().execute_with(|| { let amount: Balance = 100 * UNIT; create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); - let bob_balance_before_transfer = Assets::balance(ASSET, &BOB); + let balance_before_transfer = Assets::balance(ASSET, &BOB); assert_ok!(Fungibles::transfer(signed(ALICE), ASSET, BOB, amount / 2)); - let bob_balance_after_transfer = Assets::balance(ASSET, &BOB); - assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + amount / 2); + let balance_after_transfer = Assets::balance(ASSET, &BOB); + assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); }); } diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 7571a0ec..c62f0713 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -6,6 +6,7 @@ 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..]) { @@ -263,7 +264,7 @@ fn token_decimals_asset(asset_id: AssetId) -> u8 { fn total_supply_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!(Assets::total_supply(ASSET_ID), total_supply(addr.clone(), ASSET_ID)); @@ -280,7 +281,7 @@ fn total_supply_works() { fn balance_of_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!(Assets::balance(ASSET_ID, BOB), balance_of(addr.clone(), ASSET_ID, BOB)); @@ -297,7 +298,7 @@ fn balance_of_works() { fn allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. assert_eq!( @@ -320,7 +321,7 @@ fn allowance_works() { fn transfer_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. @@ -371,7 +372,7 @@ fn transfer_works() { fn approve_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![]); + let addr = instantiate(CONTRACT, 0, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. assert_eq!( @@ -381,8 +382,7 @@ fn approve_works() { let asset = create_asset_and_mint_to(ALICE, 0, addr.clone(), amount); assert_eq!(decoded::(approve(addr.clone(), asset, BOB, amount)), ConsumerRemaining); - let addr = - instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); + 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. @@ -412,7 +412,7 @@ fn approve_works() { fn increase_allowance_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![]); + let addr = instantiate(CONTRACT, 0, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. assert_eq!( @@ -425,8 +425,7 @@ fn increase_allowance_works() { ConsumerRemaining ); - let addr = - instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); + 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. @@ -467,7 +466,7 @@ fn increase_allowance_works() { fn token_metadata_works() { new_test_ext().execute_with(|| { let _ = env_logger::try_init(); - let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); + let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let name: Vec = vec![11, 12, 13]; let symbol: Vec = vec![21, 22, 23]; @@ -502,7 +501,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate(CONTRACT, INIT_VALUE, vec![]); // // // No tokens in circulation. // assert_eq!(Assets::asset_exists(ASSET_ID), asset_exists(addr.clone(), ASSET_ID)); @@ -519,7 +518,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate(CONTRACT, INIT_VALUE, vec![]); // let amount: Balance = 100 * UNIT; // // // Asset does not exist. @@ -579,14 +578,14 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // // Instantiate a contract without balance (relay token). -// let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 0, vec![0]); +// let addr = instantiate(CONTRACT, 0, vec![0]); // // No balance to pay for fees. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), // Module { index: 10, error: 2 }, // ); // // Instantiate a contract without balance (relay token). -// let addr = instantiate("contracts/fungibles/target/ink/fungibles.wasm", 100, vec![2]); +// let addr = instantiate(CONTRACT, 100, vec![2]); // // No balance to pay the deposit. // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), @@ -594,7 +593,7 @@ fn token_metadata_works() { // ); // // Instantiate a contract with balance. // let addr = -// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![1]); +// instantiate(CONTRACT, INIT_VALUE, vec![1]); // assert_eq!( // decoded::(create(addr.clone(), ASSET_ID, BOB, 0)), // Module { index: 52, error: 7 }, @@ -618,7 +617,7 @@ fn token_metadata_works() { // new_test_ext().execute_with(|| { // let _ = env_logger::try_init(); // let addr = -// instantiate("contracts/fungibles/target/ink/fungibles.wasm", INIT_VALUE, vec![]); +// instantiate(CONTRACT, INIT_VALUE, vec![]); // // create_asset(addr.clone(), ASSET_ID, 1); // let result = set_metadata(addr.clone(), ASSET_ID, vec![12], vec![12], 12); diff --git a/pop-api/src/lib.rs b/pop-api/src/lib.rs index d2470a92..a984bb9e 100644 --- a/pop-api/src/lib.rs +++ b/pop-api/src/lib.rs @@ -2,6 +2,7 @@ use constants::DECODING_FAILED; use ink::env::chain_extension::FromStatusCode; +#[cfg(feature = "assets")] pub use v0::assets; pub mod primitives; @@ -13,8 +14,7 @@ pub type Result = core::result::Result; mod constants { // Errors: pub(crate) const DECODING_FAILED: u32 = 255; - // TODO: Not used but will be used in the future when the remaining fungibles features will be - // implemented. + // TODO: will be used in the future when the remaining fungibles features will be implemented. pub(crate) const _MODULE_ERROR: u8 = 3; // Function IDs: From 0e49f6309553ea596c7e576979166674483ed682 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Thu, 25 Jul 2024 15:43:53 +0200 Subject: [PATCH 21/56] fix: test devnet --- runtime/devnet/src/extensions/v0.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runtime/devnet/src/extensions/v0.rs b/runtime/devnet/src/extensions/v0.rs index d9a40264..b26668f7 100644 --- a/runtime/devnet/src/extensions/v0.rs +++ b/runtime/devnet/src/extensions/v0.rs @@ -60,7 +60,7 @@ fn nested_errors(nested_error: &[u8], limit: Option) -> bool { #[cfg(test)] mod tests { use super::*; - use primitives::error::{ + use pop_primitives::error::{ ArithmeticError::*, Error::{self, *}, TokenError::*, From 8f6139c577a2a2916780cb66229eec6de3d8c1ed Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Fri, 26 Jul 2024 00:58:38 +0200 Subject: [PATCH 22/56] refactor: apply final comments --- Cargo.lock | 2 -- pallets/api/src/fungibles/benchmarking.rs | 4 +-- pallets/api/src/fungibles/mod.rs | 35 ++++++++++++++++------- pallets/api/src/fungibles/tests.rs | 3 ++ pop-api/Cargo.toml | 2 -- runtime/devnet/Cargo.toml | 1 - runtime/devnet/src/config/api.rs | 4 ++- runtime/devnet/src/extensions/mod.rs | 22 ++++++-------- runtime/devnet/src/lib.rs | 2 ++ runtime/testnet/Cargo.toml | 1 - 10 files changed, 44 insertions(+), 32 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c8f0a6c9..d3ca53e7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9731,7 +9731,6 @@ dependencies = [ "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", - "enumflags2", "env_logger 0.11.3", "frame-benchmarking", "frame-executive", @@ -9806,7 +9805,6 @@ dependencies = [ "cumulus-primitives-aura", "cumulus-primitives-core", "cumulus-primitives-utility", - "enumflags2", "env_logger 0.11.3", "frame-benchmarking", "frame-executive", diff --git a/pallets/api/src/fungibles/benchmarking.rs b/pallets/api/src/fungibles/benchmarking.rs index bb225f20..d3d65b97 100644 --- a/pallets/api/src/fungibles/benchmarking.rs +++ b/pallets/api/src/fungibles/benchmarking.rs @@ -32,8 +32,8 @@ mod benchmarks { use super::*; // Parameter: - // - 'a': whether `approve_transfer` has been called. - // - 'c': whether `cancel_approval` has been called. + // - 'a': whether `approve_transfer` is required. + // - 'c': whether `cancel_approval` is required. #[benchmark] fn approve(a: Linear<0, 1>, c: Linear<0, 1>) -> Result<(), BenchmarkError> { let asset_id = AssetIdOf::::zero(); diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 3067e1d3..cd34664a 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -1,6 +1,7 @@ /// The fungibles pallet serves as a wrapper around the pallet_assets, offering a streamlined /// interface for interacting with fungible assets. The goal is to provide a simplified, consistent /// API that adheres to standards in the smart contract space. + #[cfg(feature = "runtime-benchmarks")] mod benchmarking; #[cfg(test)] @@ -49,10 +50,22 @@ pub mod pallet { TotalSupply(AssetIdOf), /// Account balance for a given asset ID. #[codec(index = 1)] - BalanceOf(AssetIdOf, AccountIdOf), + BalanceOf { + /// The asset ID. + id: AssetIdOf, + /// The account ID of the owner. + owner: AccountIdOf, + }, /// Allowance for a spender approved by an owner, for a given asset ID. #[codec(index = 2)] - Allowance(AssetIdOf, AccountIdOf, AccountIdOf), + Allowance { + /// The asset ID. + id: AssetIdOf, + /// The account ID of the owner. + owner: AccountIdOf, + /// The account ID of the spender. + spender: AccountIdOf, + }, /// Token name for a given asset ID. #[codec(index = 8)] TokenName(AssetIdOf), @@ -69,7 +82,7 @@ pub mod pallet { pub trait Config: frame_system::Config + pallet_assets::Config { /// The instance of pallet assets it is tightly coupled to. type AssetsInstance; - /// Weight information for extrinsics in this pallet. + /// Weight information for dispatchables in this pallet. type WeightInfo: WeightInfo; } @@ -118,13 +131,14 @@ pub mod pallet { let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); let spender = T::Lookup::unlookup(spender); let id: AssetIdParameterOf = id.into(); + // If the new value is equal to the current allowance, do nothing. - if value == current_allowance { - return Ok(Some(weight(0, 0)).into()); + let return_weight = if value == current_allowance { + weight(0, 0) } // If the new value is greater than the current allowance, approve the difference - // because `approve_transfer` works additively (see pallet-assets). - if value > current_allowance { + // because `approve_transfer` works additively (see `pallet-assets`). + else if value > current_allowance { AssetsOf::::approve_transfer( origin, id, @@ -132,7 +146,7 @@ pub mod pallet { value.saturating_sub(current_allowance), ) .map_err(|e| e.with_weight(weight(1, 0)))?; - Ok(Some(weight(1, 0)).into()) + weight(1, 0) } else { // If the new value is less than the current allowance, cancel the approval and set the new value AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) @@ -141,8 +155,9 @@ pub mod pallet { return Ok(Some(weight(0, 1)).into()); } AssetsOf::::approve_transfer(origin, id, spender, value)?; - Ok(().into()) - } + weight(1, 1) + }; + Ok(Some(return_weight).into()) } /// Increases the allowance of a spender. diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index e42283d1..dbfa0b34 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -33,6 +33,9 @@ fn approve_works() { // Approves an amount to spend that is higher than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + // Approves an amount to spend that is equal to the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); + assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); // Sets allowance to zero. assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, 0)); assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), 0); diff --git a/pop-api/Cargo.toml b/pop-api/Cargo.toml index 0f878b4b..9946abf7 100644 --- a/pop-api/Cargo.toml +++ b/pop-api/Cargo.toml @@ -6,7 +6,6 @@ version = "0.0.0" edition = "2021" [dependencies] -enumflags2 = { version = "0.7.7" } ink = { version = "5.0.0", default-features = false } sp-io = { version = "31.0.0", default-features = false, features = ["disable_panic_handler", "disable_oom", "disable_allocator"] } @@ -20,7 +19,6 @@ crate-type = ["rlib"] [features] default = ["std"] std = [ - "enumflags2/std", "ink/std", "pop-primitives/std", "sp-io/std", diff --git a/runtime/devnet/Cargo.toml b/runtime/devnet/Cargo.toml index 46d23284..455a86fd 100644 --- a/runtime/devnet/Cargo.toml +++ b/runtime/devnet/Cargo.toml @@ -90,7 +90,6 @@ parachain-info.workspace = true [dev-dependencies] env_logger = "0.11.2" -enumflags2 = "0.7.9" hex = "0.4.3" rand = "0.8.5" diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index cbf7401b..ae179e4a 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -2,9 +2,11 @@ use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, Runti use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::traits::Contains; +/// A query of runtime state. #[derive(Encode, Decode, Debug, MaxEncodedLen)] #[repr(u8)] -pub enum RuntimeStateKeys { +pub enum RuntimeRead { + /// Fungible token queries. #[codec(index = 150)] Fungibles(fungibles::Read), } diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 4b1c1064..3aed89df 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -2,7 +2,7 @@ mod v0; use crate::{ config::{ - api::{AllowedApiCalls, RuntimeStateKeys}, + api::{AllowedApiCalls, RuntimeRead}, assets::TrustBackedAssetsInstance, }, fungibles::{ @@ -178,7 +178,7 @@ where let result = match key { VersionedStateRead::V0(key) => match key { - RuntimeStateKeys::Fungibles(key) => read_fungibles_state::(key, env), + RuntimeRead::Fungibles(key) => read_fungibles_state::(key, env), }, }? .encode(); @@ -189,18 +189,18 @@ where env.write(&result, false, None) } -/// Wrapper to enable versioning of `RuntimeStateKeys`. +/// Wrapper to enable versioning of runtime state reads. #[derive(Decode, Debug)] enum VersionedStateRead { - /// Version zero of reading state from api. + /// Version zero of state reads. #[codec(index = 0)] - V0(RuntimeStateKeys), + V0(RuntimeRead), } -/// Wrapper to enable versioning of `RuntimeCall`. +/// Wrapper to enable versioning of runtime calls. #[derive(Decode, Debug)] enum VersionedDispatch { - /// Version zero of dispatch calls from api. + /// Version zero of dispatch calls. #[codec(index = 0)] V0(RuntimeCall), } @@ -299,17 +299,13 @@ where env.charge_weight(T::DbWeight::get().reads(1_u64))?; match key { TotalSupply(id) => Ok(fungibles::Pallet::::total_supply(id).encode()), - BalanceOf(id, owner) => Ok(fungibles::Pallet::::balance_of(id, &owner).encode()), - Allowance(id, owner, spender) => { + BalanceOf { id, owner } => Ok(fungibles::Pallet::::balance_of(id, &owner).encode()), + Allowance { id, owner, spender } => { Ok(fungibles::Pallet::::allowance(id, &owner, &spender).encode()) }, TokenName(id) => Ok(fungibles::Pallet::::token_name(id).encode()), TokenSymbol(id) => Ok(fungibles::Pallet::::token_symbol(id).encode()), TokenDecimals(id) => Ok(fungibles::Pallet::::token_decimals(id).encode()), - // AssetsKeys::AssetExists(id) => { - // env.charge_weight(T::DbWeight::get().reads(1_u64))?; - // Ok(pallet_assets::Pallet::::asset_exists(id).encode()) - // }, } } diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 51b42892..23895310 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -966,6 +966,8 @@ mod tests { use crate::Runtime; use std::any::TypeId; + // Ensures that the account id lookup does not perform any state reads. When this changes, + // `pallet_api::fungibles` dispatchables need to be re-evaluated. #[test] fn test_lookup_config() { type ExpectedLookup = sp_runtime::traits::AccountIdLookup; diff --git a/runtime/testnet/Cargo.toml b/runtime/testnet/Cargo.toml index 79ea9cac..51cab6d6 100644 --- a/runtime/testnet/Cargo.toml +++ b/runtime/testnet/Cargo.toml @@ -90,7 +90,6 @@ parachain-info.workspace = true [dev-dependencies] env_logger = "0.11.2" hex = "0.4.3" -enumflags2 = "0.7.9" [features] default = ["std"] From 948e1448b7397d612f0205c3cba23126989d665d Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 23 Jul 2024 14:51:50 +0200 Subject: [PATCH 23/56] chore: benchmark approve --- pallets/api/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index a813a09c..6895838f 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -32,7 +32,6 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-assets/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] std = [ From 88577e42751dd45bdf205904e11abf29a8a6b180 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 23 Jul 2024 17:00:04 +0200 Subject: [PATCH 24/56] refactor: read state --- pallets/api/Cargo.toml | 1 + pallets/api/src/fungibles/mod.rs | 66 +++++++++++++++++++++++----- pop-api/src/v0/assets/fungibles.rs | 18 +++----- runtime/devnet/src/extensions/mod.rs | 34 +++----------- 4 files changed, 67 insertions(+), 52 deletions(-) diff --git a/pallets/api/Cargo.toml b/pallets/api/Cargo.toml index 6895838f..a813a09c 100644 --- a/pallets/api/Cargo.toml +++ b/pallets/api/Cargo.toml @@ -32,6 +32,7 @@ runtime-benchmarks = [ "frame-benchmarking/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", + "pallet-assets/runtime-benchmarks", "sp-runtime/runtime-benchmarks", ] std = [ diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index cd34664a..eeef53fe 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -180,61 +180,103 @@ pub mod pallet { } impl Pallet { + /// Reads the state of the fungible asset based on the provided key. + /// + /// This function matches the key to determine the type of state query and returns the + /// encoded result. + /// + /// # Arguments + /// * `key` - An instance of `FungiblesKey`, which specifies the type of state query and + /// the associated parameters. + /// + /// # Returns + /// A vector of bytes representing the encoded result of the state query. + pub fn read_state(key: Read) -> Vec { + use Read::*; + + match key { + TotalSupply(id) => Self::total_supply(id).encode(), + BalanceOf(id, owner) => Self::balance_of(id, &owner).encode(), + Allowance(id, owner, spender) => Self::allowance(id, &owner, &spender).encode(), + TokenName(id) => Self::token_name(id).encode(), + TokenSymbol(id) => Self::token_symbol(id).encode(), + TokenDecimals(id) => Self::token_decimals(id).encode(), + } + } + /// Returns the total token supply for a given asset ID. /// - /// # Parameters + /// # Arguments /// * `id` - The ID of the asset. + /// + /// # Returns + /// The total supply of the token, or an error if the operation fails. pub fn total_supply(id: AssetIdOf) -> BalanceOf { - AssetsOf::::total_supply(id) + Assets::::total_supply(id) } /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if /// the account is non-existent. /// - /// # Parameters + /// # Arguments /// * `id` - The ID of the asset. /// * `owner` - The account whose balance is being queried. + /// + /// # Returns + /// The balance of the specified account, or an error if the operation fails. pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { - AssetsOf::::balance(id, owner) + Assets::::balance(id, owner) } /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given /// asset ID. Returns `0` if no allowance has been set. /// - /// # Parameters + /// # Arguments /// * `id` - The ID of the asset. /// * `owner` - The account that owns the tokens. /// * `spender` - The account that is allowed to spend the tokens. + /// + /// # Returns + /// The remaining allowance, or an error if the operation fails. pub fn allowance( id: AssetIdOf, owner: &AccountIdOf, spender: &AccountIdOf, ) -> BalanceOf { - AssetsOf::::allowance(id, owner, spender) + Assets::::allowance(id, owner, spender) } /// Returns the token name for a given asset ID. /// - /// # Parameters + /// # Arguments /// * `id` - The ID of the asset. + /// + /// # Returns + /// The name of the token as a byte vector, or an error if the operation fails. pub fn token_name(id: AssetIdOf) -> Vec { - as MetadataInspect>>::name(id) + as MetadataInspect>>::name(id) } /// Returns the token symbol for a given asset ID. /// - /// # Parameters + /// # Arguments /// * `id` - The ID of the asset. + /// + /// # Returns + /// The symbol of the token as a byte vector, or an error if the operation fails. pub fn token_symbol(id: AssetIdOf) -> Vec { - as MetadataInspect>>::symbol(id) + as MetadataInspect>>::symbol(id) } /// Returns the token decimals for a given asset ID. /// - /// # Parameters + /// # Arguments /// * `id` - The ID of the asset. + /// + /// # Returns + /// The number of decimals of the token as a byte vector, or an error if the operation fails. pub fn token_decimals(id: AssetIdOf) -> u8 { - as MetadataInspect>>::decimals(id) + as MetadataInspect>>::decimals(id) } } } diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 42373cd7..4d2c7315 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -1,7 +1,7 @@ -use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec, scale::Decode}; +use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec}; use crate::{ - constants::{ASSETS, BALANCES, DECODING_FAILED, DISPATCH, FUNGIBLES, READ_STATE}, + constants::{ASSETS, BALANCES, DISPATCH, FUNGIBLES, READ_STATE}, primitives::{AccountId, AssetId, Balance}, v0::V0, Result, StatusCode, @@ -62,10 +62,9 @@ mod constants { pub fn total_supply(id: AssetId) -> Result { ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOTAL_SUPPLY])) .input::() - .output::>, true>() + .output::, true>() .handle_error_code::() .call(&(id)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if @@ -81,10 +80,9 @@ pub fn total_supply(id: AssetId) -> Result { pub fn balance_of(id: AssetId, owner: AccountId) -> Result { ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, BALANCE_OF])) .input::<(AssetId, AccountId)>() - .output::>, true>() + .output::, true>() .handle_error_code::() .call(&(id, owner)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given @@ -101,10 +99,9 @@ pub fn balance_of(id: AssetId, owner: AccountId) -> Result { pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result { ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, ALLOWANCE])) .input::<(AssetId, AccountId, AccountId)>() - .output::>, true>() + .output::, true>() .handle_error_code::() .call(&(id, owner, spender)) - .and_then(|v| Balance::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional @@ -217,7 +214,6 @@ pub mod metadata { .output::>, true>() .handle_error_code::() .call(&(id)) - .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the token symbol for a given asset ID. @@ -234,7 +230,6 @@ pub mod metadata { .output::>, true>() .handle_error_code::() .call(&(id)) - .and_then(|v| >::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } /// Returns the token decimals for a given asset ID. @@ -248,10 +243,9 @@ pub mod metadata { pub fn token_decimals(id: AssetId) -> Result { ChainExtensionMethod::build(u32::from_le_bytes([V0, READ_STATE, FUNGIBLES, TOKEN_DECIMALS])) .input::() - .output::>, true>() + .output::, true>() .handle_error_code::() .call(&(id)) - .and_then(|v| ::decode(&mut &v[..]).map_err(|_e| StatusCode(DECODING_FAILED))) } } diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 3aed89df..228534e9 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -169,19 +169,21 @@ where { const LOG_PREFIX: &str = " read_state |"; - // Prefix params with version, pallet, index to simplify decoding. + // Prefix params with version, pallet, index to simplify decoding, and decode parameters for + // reading state. params.insert(0, version); params.insert(1, pallet_index); params.insert(2, call_index); let key = >::decode(&mut ¶ms[..]) .map_err(|_| DispatchError::Other("DecodingFailed"))?; + // Charge weight for doing one storage read. + env.charge_weight(T::DbWeight::get().reads(1_u64))?; let result = match key { VersionedStateRead::V0(key) => match key { - RuntimeRead::Fungibles(key) => read_fungibles_state::(key, env), + RuntimeStateKeys::Fungibles(key) => fungibles::Pallet::::read_state(key), }, - }? - .encode(); + }; log::trace!( target:LOG_TARGET, "{} result: {:?}.", LOG_PREFIX, result @@ -285,30 +287,6 @@ impl TryFrom for FuncId { } } -fn read_fungibles_state( - key: Read, - env: &mut Environment, -) -> Result, DispatchError> -where - T: pallet_contracts::Config - + pallet_assets::Config - + fungibles::Config, - E: Ext, - T: frame_system::Config, -{ - env.charge_weight(T::DbWeight::get().reads(1_u64))?; - match key { - TotalSupply(id) => Ok(fungibles::Pallet::::total_supply(id).encode()), - BalanceOf { id, owner } => Ok(fungibles::Pallet::::balance_of(id, &owner).encode()), - Allowance { id, owner, spender } => { - Ok(fungibles::Pallet::::allowance(id, &owner, &spender).encode()) - }, - TokenName(id) => Ok(fungibles::Pallet::::token_name(id).encode()), - TokenSymbol(id) => Ok(fungibles::Pallet::::token_symbol(id).encode()), - TokenDecimals(id) => Ok(fungibles::Pallet::::token_decimals(id).encode()), - } -} - #[cfg(test)] mod tests { use super::*; From 809e1920aa0d647cc9639c03ac0b788c140eb773 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 23 Jul 2024 21:21:17 +0200 Subject: [PATCH 25/56] refactor: add state query filter --- runtime/devnet/src/config/api.rs | 33 +++++++++++++++++++++++----- runtime/devnet/src/extensions/mod.rs | 11 ++++++---- runtime/devnet/src/lib.rs | 2 ++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index ae179e4a..5c5da339 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -16,18 +16,39 @@ impl fungibles::Config for Runtime { type WeightInfo = fungibles::weights::SubstrateWeight; } -/// A type to identify allowed calls to the Runtime from contracts. Used by Pop API +/// A type to identify allowed calls to the Runtime from the API. pub struct AllowedApiCalls; impl Contains for AllowedApiCalls { + /// Allowed runtime calls from the API. fn contains(c: &RuntimeCall) -> bool { - use fungibles::Call as FungiblesCall; + use fungibles::Call::*; matches!( c, - RuntimeCall::Fungibles( - FungiblesCall::transfer { .. } - | FungiblesCall::approve { .. } - | FungiblesCall::increase_allowance { .. } + RuntimeCall::Fungibles(transfer { .. } | approve { .. } | increase_allowance { .. }) + ) + } +} + +/// State queries that can be made in the API. +#[derive(Encode, Decode, Debug, MaxEncodedLen)] +#[repr(u8)] +pub enum RuntimeStateKeys { + #[codec(index = 150)] + Fungibles(fungibles::FungiblesKey), +} + +impl Contains> for AllowedApiCalls { + /// Allowed state queries from the API. + fn contains(c: &RuntimeStateKeys) -> bool { + use fungibles::FungiblesKey::*; + matches!( + c, + RuntimeStateKeys::Fungibles( + TotalSupply(..) + | BalanceOf(..) | Allowance(..) + | TokenName(..) | TokenSymbol(..) + | TokenDecimals(..) ) ) } diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index 228534e9..d8c5b15d 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -180,8 +180,11 @@ where // Charge weight for doing one storage read. env.charge_weight(T::DbWeight::get().reads(1_u64))?; let result = match key { - VersionedStateRead::V0(key) => match key { - RuntimeStateKeys::Fungibles(key) => fungibles::Pallet::::read_state(key), + VersionedStateRead::V0(key) => { + ensure!(AllowedApiCalls::contains(&key), DispatchError::Other("UnknownCall")); + match key { + RuntimeStateKeys::Fungibles(key) => fungibles::Pallet::::read_state(key), + } }, }; log::trace!( @@ -220,10 +223,10 @@ enum VersionedDispatch { // - `error`: The `DispatchError` encountered during contract execution. // - `version`: The version of the chain extension, used to determine the known errors. pub(crate) fn convert_to_status_code(error: DispatchError, version: u8) -> u32 { - // "UnknownFunctionId" and "DecodingFailed" are mapped to specific errors in the API and will + // "UnknownCall" and "DecodingFailed" are mapped to specific errors in the API and will // never change. let mut encoded_error = match error { - DispatchError::Other("UnknownFunctionId") => Vec::from([254u8, 0, 0, 0]), + DispatchError::Other("UnknownCall") => Vec::from([254u8, 0, 0, 0]), DispatchError::Other("DecodingFailed") => Vec::from([255u8, 0, 0, 0]), _ => error.encode(), }; diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 23895310..557d2dcc 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -11,6 +11,7 @@ mod weights; // Public due to integration tests crate. pub mod config; +use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use smallvec::smallvec; @@ -252,6 +253,7 @@ impl Contains for FilteredCalls { } } + /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from /// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), /// but overridden as needed. From ac9714cbdc96fecdfe6154dca972adedf686c58f Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 23 Jul 2024 22:39:48 +0200 Subject: [PATCH 26/56] fix: test UnknownCall --- runtime/devnet/src/extensions/v0.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runtime/devnet/src/extensions/v0.rs b/runtime/devnet/src/extensions/v0.rs index b26668f7..72760323 100644 --- a/runtime/devnet/src/extensions/v0.rs +++ b/runtime/devnet/src/extensions/v0.rs @@ -76,7 +76,7 @@ mod tests { DispatchError::Other(""), (Other { dispatch_error_index: 0, error_index: 0, error: 0 }), ), - (DispatchError::Other("UnknownFunctionId"), UnknownCall), + (DispatchError::Other("UnknownCall"), UnknownCall), (DispatchError::Other("DecodingFailed"), DecodingFailed), (DispatchError::CannotLookup, CannotLookup), (DispatchError::BadOrigin, BadOrigin), @@ -120,7 +120,7 @@ mod tests { DispatchError::Other("Random"), (Other { dispatch_error_index: 0, error_index: 0, error: 0 }), ), - (DispatchError::Other("UnknownFunctionId"), UnknownCall), + (DispatchError::Other("UnknownCall"), UnknownCall), (DispatchError::Other("DecodingFailed"), DecodingFailed), ]; for (dispatch_error, expected) in test_cases { From 77711ecd7f0e269e71079fc49281e7867cbe8226 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Fri, 26 Jul 2024 00:02:38 +0200 Subject: [PATCH 27/56] refactor: read state functions --- pallets/api/src/fungibles/mod.rs | 98 +++++------------------------- pallets/api/src/fungibles/tests.rs | 20 +++--- runtime/devnet/src/config/api.rs | 20 ++---- runtime/devnet/src/lib.rs | 1 - 4 files changed, 32 insertions(+), 107 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index eeef53fe..4d67e6ac 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -188,95 +188,25 @@ pub mod pallet { /// # Arguments /// * `key` - An instance of `FungiblesKey`, which specifies the type of state query and /// the associated parameters. - /// - /// # Returns - /// A vector of bytes representing the encoded result of the state query. pub fn read_state(key: Read) -> Vec { use Read::*; match key { - TotalSupply(id) => Self::total_supply(id).encode(), - BalanceOf(id, owner) => Self::balance_of(id, &owner).encode(), - Allowance(id, owner, spender) => Self::allowance(id, &owner, &spender).encode(), - TokenName(id) => Self::token_name(id).encode(), - TokenSymbol(id) => Self::token_symbol(id).encode(), - TokenDecimals(id) => Self::token_decimals(id).encode(), + TotalSupply(id) => AssetsOf::::total_supply(id).encode(), + BalanceOf(id, owner) => AssetsOf::::balance(id, owner).encode(), + Allowance(id, owner, spender) => { + AssetsOf::::allowance(id, &owner, &spender).encode() + }, + TokenName(id) => { + as MetadataInspect>>::name(id).encode() + }, + TokenSymbol(id) => { + as MetadataInspect>>::symbol(id).encode() + }, + TokenDecimals(id) => { + as MetadataInspect>>::decimals(id).encode() + }, } } - - /// Returns the total token supply for a given asset ID. - /// - /// # Arguments - /// * `id` - The ID of the asset. - /// - /// # Returns - /// The total supply of the token, or an error if the operation fails. - pub fn total_supply(id: AssetIdOf) -> BalanceOf { - Assets::::total_supply(id) - } - - /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if - /// the account is non-existent. - /// - /// # Arguments - /// * `id` - The ID of the asset. - /// * `owner` - The account whose balance is being queried. - /// - /// # Returns - /// The balance of the specified account, or an error if the operation fails. - pub fn balance_of(id: AssetIdOf, owner: &AccountIdOf) -> BalanceOf { - Assets::::balance(id, owner) - } - - /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given - /// asset ID. Returns `0` if no allowance has been set. - /// - /// # Arguments - /// * `id` - The ID of the asset. - /// * `owner` - The account that owns the tokens. - /// * `spender` - The account that is allowed to spend the tokens. - /// - /// # Returns - /// The remaining allowance, or an error if the operation fails. - pub fn allowance( - id: AssetIdOf, - owner: &AccountIdOf, - spender: &AccountIdOf, - ) -> BalanceOf { - Assets::::allowance(id, owner, spender) - } - - /// Returns the token name for a given asset ID. - /// - /// # Arguments - /// * `id` - The ID of the asset. - /// - /// # Returns - /// The name of the token as a byte vector, or an error if the operation fails. - pub fn token_name(id: AssetIdOf) -> Vec { - as MetadataInspect>>::name(id) - } - - /// Returns the token symbol for a given asset ID. - /// - /// # Arguments - /// * `id` - The ID of the asset. - /// - /// # Returns - /// The symbol of the token as a byte vector, or an error if the operation fails. - pub fn token_symbol(id: AssetIdOf) -> Vec { - as MetadataInspect>>::symbol(id) - } - - /// Returns the token decimals for a given asset ID. - /// - /// # Arguments - /// * `id` - The ID of the asset. - /// - /// # Returns - /// The number of decimals of the token as a byte vector, or an error if the operation fails. - pub fn token_decimals(id: AssetIdOf) -> u8 { - as MetadataInspect>>::decimals(id) - } } } diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index dbfa0b34..1dec207a 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -1,4 +1,5 @@ -use crate::mock::*; +use crate::{fungibles::Read::*, mock::*}; +use codec::Encode; use frame_support::{ assert_ok, traits::fungibles::{approvals::Inspect, metadata::Inspect as MetadataInspect}, @@ -60,7 +61,7 @@ fn increase_allowance_works() { fn total_supply_works() { new_test_ext().execute_with(|| { create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); - assert_eq!(Assets::total_supply(ASSET), Fungibles::total_supply(ASSET)); + assert_eq!(Assets::total_supply(ASSET).encode(), Fungibles::read_state(TotalSupply(ASSET))); }); } @@ -68,7 +69,10 @@ fn total_supply_works() { fn balance_of_works() { new_test_ext().execute_with(|| { create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); - assert_eq!(Assets::balance(ASSET, ALICE), Fungibles::balance_of(ASSET, &ALICE)); + assert_eq!( + Assets::balance(ASSET, ALICE).encode(), + Fungibles::read_state(BalanceOf(ASSET, ALICE)) + ); }); } @@ -77,8 +81,8 @@ fn allowance_works() { new_test_ext().execute_with(|| { create_asset_mint_and_approve(ALICE, ASSET, BOB, 100, ALICE, 50); assert_eq!( - Assets::allowance(ASSET, &ALICE, &BOB), - Fungibles::allowance(ASSET, &ALICE, &BOB) + Assets::allowance(ASSET, &ALICE, &BOB).encode(), + Fungibles::read_state(Allowance(ASSET, ALICE, BOB)) ); }); } @@ -90,9 +94,9 @@ fn token_metadata_works() { let symbol: Vec = vec![21, 22, 23]; let decimals: u8 = 69; create_asset_and_set_metadata(ALICE, ASSET, name.clone(), symbol.clone(), decimals); - assert_eq!(Assets::name(ASSET), Fungibles::token_name(ASSET)); - assert_eq!(Assets::symbol(ASSET), Fungibles::token_symbol(ASSET)); - assert_eq!(Assets::decimals(ASSET), Fungibles::token_decimals(ASSET)); + assert_eq!(Assets::name(ASSET).encode(), Fungibles::read_state(TokenName(ASSET))); + assert_eq!(Assets::symbol(ASSET).encode(), Fungibles::read_state(TokenSymbol(ASSET))); + assert_eq!(Assets::decimals(ASSET).encode(), Fungibles::read_state(TokenDecimals(ASSET))); }); } diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 5c5da339..1e430764 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -11,11 +11,6 @@ pub enum RuntimeRead { Fungibles(fungibles::Read), } -impl fungibles::Config for Runtime { - type AssetsInstance = TrustBackedAssetsInstance; - type WeightInfo = fungibles::weights::SubstrateWeight; -} - /// A type to identify allowed calls to the Runtime from the API. pub struct AllowedApiCalls; @@ -30,18 +25,10 @@ impl Contains for AllowedApiCalls { } } -/// State queries that can be made in the API. -#[derive(Encode, Decode, Debug, MaxEncodedLen)] -#[repr(u8)] -pub enum RuntimeStateKeys { - #[codec(index = 150)] - Fungibles(fungibles::FungiblesKey), -} - impl Contains> for AllowedApiCalls { /// Allowed state queries from the API. fn contains(c: &RuntimeStateKeys) -> bool { - use fungibles::FungiblesKey::*; + use fungibles::Read::*; matches!( c, RuntimeStateKeys::Fungibles( @@ -53,3 +40,8 @@ impl Contains> for AllowedApiCalls { ) } } + +impl fungibles::Config for Runtime { + type AssetsInstance = TrustBackedAssetsInstance; + type WeightInfo = fungibles::weights::SubstrateWeight; +} diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 557d2dcc..122e8e24 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -253,7 +253,6 @@ impl Contains for FilteredCalls { } } - /// The default types are being injected by [`derive_impl`](`frame_support::derive_impl`) from /// [`ParaChainDefaultConfig`](`struct@frame_system::config_preludes::ParaChainDefaultConfig`), /// but overridden as needed. From b50830cb3925593185fc46edb8e144c3403923f9 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Fri, 26 Jul 2024 01:31:38 +0200 Subject: [PATCH 28/56] refactor: enums --- pallets/api/src/fungibles/mod.rs | 4 ++-- pallets/api/src/fungibles/tests.rs | 4 ++-- runtime/devnet/src/config/api.rs | 8 ++++---- runtime/devnet/src/extensions/mod.rs | 7 ++----- runtime/devnet/src/lib.rs | 1 - 5 files changed, 10 insertions(+), 14 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 4d67e6ac..6ed2cfc6 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -193,8 +193,8 @@ pub mod pallet { match key { TotalSupply(id) => AssetsOf::::total_supply(id).encode(), - BalanceOf(id, owner) => AssetsOf::::balance(id, owner).encode(), - Allowance(id, owner, spender) => { + BalanceOf { id, owner } => AssetsOf::::balance(id, owner).encode(), + Allowance { id, owner, spender } => { AssetsOf::::allowance(id, &owner, &spender).encode() }, TokenName(id) => { diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 1dec207a..c8de1020 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -71,7 +71,7 @@ fn balance_of_works() { create_asset_and_mint_to(ALICE, ASSET, ALICE, 100); assert_eq!( Assets::balance(ASSET, ALICE).encode(), - Fungibles::read_state(BalanceOf(ASSET, ALICE)) + Fungibles::read_state(BalanceOf { id: ASSET, owner: ALICE }) ); }); } @@ -82,7 +82,7 @@ fn allowance_works() { create_asset_mint_and_approve(ALICE, ASSET, BOB, 100, ALICE, 50); assert_eq!( Assets::allowance(ASSET, &ALICE, &BOB).encode(), - Fungibles::read_state(Allowance(ASSET, ALICE, BOB)) + Fungibles::read_state(Allowance { id: ASSET, owner: ALICE, spender: BOB }) ); }); } diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 1e430764..884128f2 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -25,15 +25,15 @@ impl Contains for AllowedApiCalls { } } -impl Contains> for AllowedApiCalls { +impl Contains> for AllowedApiCalls { /// Allowed state queries from the API. - fn contains(c: &RuntimeStateKeys) -> bool { + fn contains(c: &RuntimeRead) -> bool { use fungibles::Read::*; matches!( c, - RuntimeStateKeys::Fungibles( + RuntimeRead::Fungibles( TotalSupply(..) - | BalanceOf(..) | Allowance(..) + | BalanceOf { .. } | Allowance { .. } | TokenName(..) | TokenSymbol(..) | TokenDecimals(..) ) diff --git a/runtime/devnet/src/extensions/mod.rs b/runtime/devnet/src/extensions/mod.rs index d8c5b15d..486b046b 100644 --- a/runtime/devnet/src/extensions/mod.rs +++ b/runtime/devnet/src/extensions/mod.rs @@ -5,10 +5,7 @@ use crate::{ api::{AllowedApiCalls, RuntimeRead}, assets::TrustBackedAssetsInstance, }, - fungibles::{ - self, - Read::{self, *}, - }, + fungibles::{self}, AccountId, RuntimeCall, RuntimeOrigin, }; use codec::{Decode, Encode}; @@ -183,7 +180,7 @@ where VersionedStateRead::V0(key) => { ensure!(AllowedApiCalls::contains(&key), DispatchError::Other("UnknownCall")); match key { - RuntimeStateKeys::Fungibles(key) => fungibles::Pallet::::read_state(key), + RuntimeRead::Fungibles(key) => fungibles::Pallet::::read_state(key), } }, }; diff --git a/runtime/devnet/src/lib.rs b/runtime/devnet/src/lib.rs index 122e8e24..23895310 100644 --- a/runtime/devnet/src/lib.rs +++ b/runtime/devnet/src/lib.rs @@ -11,7 +11,6 @@ mod weights; // Public due to integration tests crate. pub mod config; -use codec::{Decode, Encode, MaxEncodedLen}; use cumulus_pallet_parachain_system::RelayNumberMonotonicallyIncreases; use polkadot_runtime_common::xcm_sender::NoPriceForMessageDelivery; use smallvec::smallvec; From b93f0090f4f702eaef2af772db4c46182b7b27ab Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Fri, 26 Jul 2024 17:58:13 +0200 Subject: [PATCH 29/56] refactor: api integration tests --- .../integration-tests/src/local_fungibles.rs | 616 +++++++++--------- 1 file changed, 291 insertions(+), 325 deletions(-) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 6de57ca4..b41508ab 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -8,246 +8,6 @@ use pop_primitives::error::{ const ASSET_ID: AssetId = 1; const CONTRACT: &str = "contracts/fungibles/target/ink/fungibles.wasm"; -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. -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).unwrap() -} - -// Call balance_of contract message. -fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Balance { - 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).unwrap() -} - -// Call allowance contract message. -fn allowance( - addr: AccountId32, - asset_id: AssetId, - owner: AccountId32, - spender: AccountId32, -) -> Balance { - 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).unwrap() -} - -// Call token_name contract message. -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).unwrap() -} - -// Call token_symbol contract message. -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).unwrap() -} - -// Call token_decimals contract message. -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).unwrap() -} - -fn transfer( - addr: AccountId32, - asset_id: AssetId, - to: AccountId32, - value: Balance, -) -> ExecReturnValue { - let function = function_selector("transfer"); - let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - result -} - -fn approve( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, - value: Balance, -) -> ExecReturnValue { - let function = function_selector("approve"); - let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - result -} - -fn increase_allowance( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, - value: Balance, -) -> ExecReturnValue { - let function = function_selector("increase_allowance"); - let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - result -} - -// fn asset_exists(addr: AccountId32, asset_id: AssetId) -> bool { -// let function = function_selector("asset_exists"); -// let params = [function, asset_id.encode()].concat(); -// let result = bare_call(addr, params, 0).expect("should work"); -// decoded::(result) -// } -// -// fn create( -// addr: AccountId32, -// asset_id: AssetId, -// admin: AccountId32, -// min_balance: Balance, -// ) -> ExecReturnValue { -// let function = function_selector("create"); -// let params = [function, asset_id.encode(), admin.encode(), min_balance.encode()].concat(); -// bare_call(addr, params, 0).expect("should work") -// } -// -// fn set_metadata( -// addr: AccountId32, -// asset_id: AssetId, -// name: Vec, -// symbol: Vec, -// decimals: u8, -// ) -> ExecReturnValue { -// let function = function_selector("set_metadata"); -// let params = -// [function, asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); -// bare_call(addr, params, 0).expect("should work") -// } - -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 -} - -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 -} - -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. -fn create_asset_mint_and_approve( - owner: AccountId32, - asset_id: AssetId, - to: AccountId32, - mint: Balance, - spender: AccountId32, - approve: Balance, -) { - 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, - )); -} - -// Freeze an asset. -fn freeze_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} - -// Thaw an asset. -fn thaw_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} - -// Start destroying an asset. -fn start_destroy_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} - -// Create an asset and set metadata. -fn create_asset_and_set_metadata( - owner: AccountId32, - asset_id: AssetId, - name: Vec, - symbol: Vec, - decimals: u8, -) { - assert_ok!(Assets::create( - RuntimeOrigin::signed(owner.clone()), - asset_id.into(), - owner.clone().into(), - 100 - )); - set_metadata_asset(owner, asset_id, name, symbol, decimals); -} - -// Set metadata of an asset. -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 - )); -} - -fn token_name_asset(asset_id: AssetId) -> Vec { - as MetadataInspect>::name( - asset_id, - ) -} - -fn token_symbol_asset(asset_id: AssetId) -> Vec { - as MetadataInspect>::symbol( - asset_id, - ) -} - -fn token_decimals_asset(asset_id: AssetId) -> u8 { - as MetadataInspect>::decimals( - asset_id, - ) -} - /// 1. PSP-22 Interface: /// - total_supply /// - balance_of @@ -265,13 +25,13 @@ fn total_supply_works() { let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. - assert_eq!(Assets::total_supply(ASSET_ID), total_supply(addr.clone(), ASSET_ID)); - assert_eq!(0, total_supply(addr.clone(), ASSET_ID)); + assert_eq!(total_supply(addr.clone(), ASSET_ID), Ok(Assets::total_supply(ASSET_ID))); + assert_eq!(total_supply(addr.clone(), ASSET_ID), Ok(0)); // Tokens in circulation. create_asset_and_mint_to(addr.clone(), ASSET_ID, BOB, 100); - assert_eq!(Assets::total_supply(ASSET_ID), total_supply(addr.clone(), ASSET_ID)); - assert_eq!(100, total_supply(addr, ASSET_ID)); + assert_eq!(total_supply(addr.clone(), ASSET_ID), Ok(Assets::total_supply(ASSET_ID))); + assert_eq!(total_supply(addr, ASSET_ID), Ok(100)); }); } @@ -282,13 +42,13 @@ fn balance_of_works() { let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); // No tokens in circulation. - assert_eq!(Assets::balance(ASSET_ID, BOB), balance_of(addr.clone(), ASSET_ID, BOB)); - assert_eq!(0, balance_of(addr.clone(), ASSET_ID, BOB)); + assert_eq!(balance_of(addr.clone(), ASSET_ID, BOB), Ok(Assets::balance(ASSET_ID, BOB))); + assert_eq!(balance_of(addr.clone(), ASSET_ID, BOB), Ok(0)); // Tokens in circulation. create_asset_and_mint_to(addr.clone(), ASSET_ID, BOB, 100); - assert_eq!(Assets::balance(ASSET_ID, BOB), balance_of(addr.clone(), ASSET_ID, BOB)); - assert_eq!(100, balance_of(addr, ASSET_ID, BOB)); + assert_eq!(balance_of(addr.clone(), ASSET_ID, BOB), Ok(Assets::balance(ASSET_ID, BOB))); + assert_eq!(balance_of(addr, ASSET_ID, BOB), Ok(100)); }); } @@ -300,18 +60,18 @@ fn allowance_works() { // No tokens in circulation. assert_eq!( - Assets::allowance(ASSET_ID, &BOB, &ALICE), - allowance(addr.clone(), ASSET_ID, BOB, ALICE) + allowance(addr.clone(), ASSET_ID, BOB, ALICE), + Ok(Assets::allowance(ASSET_ID, &BOB, &ALICE)) ); - assert_eq!(0, allowance(addr.clone(), ASSET_ID, BOB, ALICE)); + assert_eq!(allowance(addr.clone(), ASSET_ID, BOB, ALICE), Ok(0)); // Tokens in circulation. create_asset_mint_and_approve(addr.clone(), ASSET_ID, BOB, 100, ALICE, 50); assert_eq!( - Assets::allowance(ASSET_ID, &BOB, &ALICE), - allowance(addr.clone(), ASSET_ID, BOB, ALICE) + allowance(addr.clone(), ASSET_ID, BOB, ALICE), + Ok(Assets::allowance(ASSET_ID, &BOB, &ALICE)) ); - assert_eq!(50, allowance(addr, ASSET_ID, BOB, ALICE)); + assert_eq!(allowance(addr, ASSET_ID, BOB, ALICE), Ok(50)); }); } @@ -321,47 +81,36 @@ fn transfer_works() { let _ = env_logger::try_init(); let addr = instantiate(CONTRACT, INIT_VALUE, vec![]); let amount: Balance = 100 * UNIT; - // Asset does not exist. - assert_eq!( - decoded::(transfer(addr.clone(), 1, BOB, amount,)), - Ok(Module { index: 52, error: 3 }), - ); + assert_eq!(transfer(addr.clone(), 1, BOB, amount), Err(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); // Asset is not live, i.e. frozen or being destroyed. freeze_asset(ALICE, asset); assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount,)), - Ok(Module { index: 52, error: 16 }), + transfer(addr.clone(), asset, BOB, amount), + Err(Module { index: 52, error: 16 }) ); thaw_asset(ALICE, asset); // Not enough balance. assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount + 1 * UNIT)), - Ok(Module { index: 52, error: 0 }), + transfer(addr.clone(), asset, BOB, amount + 1 * UNIT), + Err(Module { index: 52, error: 0 }) ); // Not enough balance due to ED. - assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount)), - Ok(Module { index: 52, error: 0 }), - ); + assert_eq!(transfer(addr.clone(), asset, BOB, amount), Err(Module { index: 52, error: 0 })); // Successful transfer. let balance_before_transfer = Assets::balance(asset, &BOB); - let result = transfer(addr.clone(), asset, BOB, amount / 2); - assert!(!result.did_revert(), "Contract reverted!"); + assert_ok!(transfer(addr.clone(), asset, BOB, amount / 2)); let balance_after_transfer = Assets::balance(asset, &BOB); assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); // Transfer asset to account that does not exist. - assert_eq!( - decoded::(transfer(addr.clone(), asset, FERDIE, amount / 4)), - Ok(Token(CannotCreate)) - ); + 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); assert_eq!( - decoded::(transfer(addr.clone(), asset, BOB, amount / 4)), - Ok(Module { index: 52, error: 16 }), + transfer(addr.clone(), asset, BOB, amount / 4), + Err(Module { index: 52, error: 16 }) ); }); } @@ -373,39 +122,26 @@ fn approve_works() { let addr = instantiate(CONTRACT, 0, vec![]); let amount: Balance = 100 * UNIT; // Asset does not exist. - assert_eq!( - decoded::(approve(addr.clone(), 0, BOB, amount)), - Ok(Module { index: 52, error: 3 }), - ); + assert_eq!(approve(addr.clone(), 0, BOB, amount), Err(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)), - Ok(ConsumerRemaining) - ); - + assert_eq!(approve(addr.clone(), asset, 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!( - decoded::(approve(addr.clone(), asset, BOB, amount)), - Ok(Module { index: 52, error: 16 }), - ); + assert_eq!(approve(addr.clone(), asset, BOB, amount), Err(Module { index: 52, error: 16 })); thaw_asset(ALICE, asset); // Successful approvals: assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); - assert!(!approve(addr.clone(), asset, BOB, amount).did_revert(), "Contract reverted!"); + assert_ok!(approve(addr.clone(), asset, BOB, amount)); assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); // Non-additive, sets new value. - assert!(!approve(addr.clone(), asset, BOB, amount / 2).did_revert(), "Contract reverted!"); + 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!( - decoded::(approve(addr.clone(), asset, BOB, amount)), - Ok(Module { index: 52, error: 16 }), - ); + assert_eq!(approve(addr.clone(), asset, BOB, amount), Err(Module { index: 52, error: 16 })); }); } @@ -417,43 +153,33 @@ fn increase_allowance_works() { let amount: Balance = 100 * UNIT; // Asset does not exist. assert_eq!( - decoded::(increase_allowance(addr.clone(), 0, BOB, amount)), - Ok(Module { index: 52, error: 3 }), + increase_allowance(addr.clone(), 0, BOB, amount), + Err(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)), - Ok(ConsumerRemaining) - ); - + assert_eq!(increase_allowance(addr.clone(), asset, 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!( - decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - Ok(Module { index: 52, error: 16 }), + increase_allowance(addr.clone(), asset, BOB, amount), + Err(Module { index: 52, error: 16 }) ); thaw_asset(ALICE, asset); // Successful approvals: assert_eq!(0, Assets::allowance(asset, &addr, &BOB)); - assert!( - !increase_allowance(addr.clone(), asset, BOB, amount).did_revert(), - "Contract reverted!" - ); + assert_ok!(increase_allowance(addr.clone(), asset, BOB, amount)); assert_eq!(Assets::allowance(asset, &addr, &BOB), amount); // Additive. - assert!( - !increase_allowance(addr.clone(), asset, BOB, amount).did_revert(), - "Contract reverted!" - ); + 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_eq!( - decoded::(increase_allowance(addr.clone(), asset, BOB, amount)), - Ok(Module { index: 52, error: 16 }), + increase_allowance(addr.clone(), asset, BOB, amount), + Err(Module { index: 52, error: 16 }) ); }); } @@ -472,13 +198,14 @@ fn token_metadata_works() { 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_asset(ASSET_ID), token_name(addr.clone(), ASSET_ID)); - assert_eq!(Vec::::new(), token_name(addr.clone(), ASSET_ID)); - assert_eq!(token_symbol_asset(ASSET_ID), token_symbol(addr.clone(), ASSET_ID)); - assert_eq!(Vec::::new(), token_symbol(addr.clone(), ASSET_ID)); - assert_eq!(token_decimals_asset(ASSET_ID), token_decimals(addr.clone(), ASSET_ID)); - assert_eq!(0, token_decimals(addr.clone(), ASSET_ID)); + assert_eq!(token_name(addr.clone(), ASSET_ID), Ok(token_name_asset(ASSET_ID))); + assert_eq!(token_name(addr.clone(), ASSET_ID), Ok(Vec::::new())); + assert_eq!(token_symbol(addr.clone(), ASSET_ID), Ok(token_symbol_asset(ASSET_ID))); + assert_eq!(token_symbol(addr.clone(), ASSET_ID), Ok(Vec::::new())); + assert_eq!(token_decimals(addr.clone(), ASSET_ID), Ok(token_decimals_asset(ASSET_ID))); + assert_eq!(token_decimals(addr.clone(), ASSET_ID), Ok(0)); create_asset_and_set_metadata( addr.clone(), @@ -487,12 +214,12 @@ fn token_metadata_works() { symbol.clone(), decimals, ); - assert_eq!(token_name_asset(ASSET_ID), token_name(addr.clone(), ASSET_ID)); - assert_eq!(name, token_name(addr.clone(), ASSET_ID)); - assert_eq!(token_symbol_asset(ASSET_ID), token_symbol(addr.clone(), ASSET_ID)); - assert_eq!(symbol, token_symbol(addr.clone(), ASSET_ID)); - assert_eq!(token_decimals_asset(ASSET_ID), token_decimals(addr.clone(), ASSET_ID)); - assert_eq!(decimals, token_decimals(addr.clone(), ASSET_ID)); + assert_eq!(token_name(addr.clone(), ASSET_ID), Ok(token_name_asset(ASSET_ID))); + assert_eq!(token_name(addr.clone(), ASSET_ID), Ok(name)); + assert_eq!(token_symbol(addr.clone(), ASSET_ID), Ok(token_symbol_asset(ASSET_ID))); + assert_eq!(token_symbol(addr.clone(), ASSET_ID), Ok(symbol)); + assert_eq!(token_decimals(addr.clone(), ASSET_ID), Ok(token_decimals_asset(ASSET_ID))); + assert_eq!(token_decimals(addr.clone(), ASSET_ID), Ok(decimals)); }); } @@ -625,3 +352,242 @@ fn token_metadata_works() { // assert!(!result.did_revert(), "Contract reverted!"); // }); // } + +fn decoded(result: ExecReturnValue) -> T { + ::decode(&mut &result.data[1..]).expect("_") +} + +// Call total_supply contract message. +fn total_supply(addr: AccountId32, asset_id: AssetId) -> Result { + 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) +} + +// Call balance_of contract message. +fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Result { + 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) +} + +// Call allowance contract message. +fn allowance( + addr: AccountId32, + asset_id: AssetId, + owner: AccountId32, + spender: AccountId32, +) -> Result { + 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) +} + +// Call token_name contract message. +fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { + let function = function_selector("token_name"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::, Error>>(result) +} + +// Call token_symbol contract message. +fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> { + let function = function_selector("token_symbol"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::, Error>>(result) +} + +// Call token_decimals contract message. +fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { + 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) +} + +fn transfer( + addr: AccountId32, + asset_id: AssetId, + to: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("transfer"); + let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result) +} + +fn approve( + addr: AccountId32, + asset_id: AssetId, + spender: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("approve"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result) +} + +fn increase_allowance( + addr: AccountId32, + asset_id: AssetId, + spender: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("increase_allowance"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result) +} + +// fn asset_exists(addr: AccountId32, asset_id: AssetId) -> bool { +// let function = function_selector("asset_exists"); +// let params = [function, asset_id.encode()].concat(); +// let result = bare_call(addr, params, 0).expect("should work"); +// decoded::(result) +// } +// +// fn create( +// addr: AccountId32, +// asset_id: AssetId, +// admin: AccountId32, +// min_balance: Balance, +// ) -> ExecReturnValue { +// let function = function_selector("create"); +// let params = [function, asset_id.encode(), admin.encode(), min_balance.encode()].concat(); +// bare_call(addr, params, 0).expect("should work") +// } +// +// fn set_metadata( +// addr: AccountId32, +// asset_id: AssetId, +// name: Vec, +// symbol: Vec, +// decimals: u8, +// ) -> ExecReturnValue { +// let function = function_selector("set_metadata"); +// let params = +// [function, asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); +// bare_call(addr, params, 0).expect("should work") +// } + +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 +} + +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 +} + +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. +fn create_asset_mint_and_approve( + owner: AccountId32, + asset_id: AssetId, + to: AccountId32, + mint: Balance, + spender: AccountId32, + approve: Balance, +) { + 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, + )); +} + +// Freeze an asset. +fn freeze_asset(owner: AccountId32, asset_id: AssetId) { + assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); +} + +// Thaw an asset. +fn thaw_asset(owner: AccountId32, asset_id: AssetId) { + assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); +} + +// Start destroying an asset. +fn start_destroy_asset(owner: AccountId32, asset_id: AssetId) { + assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(owner.into()), asset_id.into())); +} + +// Create an asset and set metadata. +fn create_asset_and_set_metadata( + owner: AccountId32, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) { + assert_ok!(Assets::create( + RuntimeOrigin::signed(owner.clone()), + asset_id.into(), + owner.clone().into(), + 100 + )); + set_metadata_asset(owner, asset_id, name, symbol, decimals); +} + +// Set metadata of an asset. +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 + )); +} + +fn token_name_asset(asset_id: AssetId) -> Vec { + as MetadataInspect>::name( + asset_id, + ) +} + +fn token_symbol_asset(asset_id: AssetId) -> Vec { + as MetadataInspect>::symbol( + asset_id, + ) +} + +fn token_decimals_asset(asset_id: AssetId) -> u8 { + as MetadataInspect>::decimals( + asset_id, + ) +} From b639e8b053d26603b0e114bd23a34a6ee3468bcb Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Fri, 26 Jul 2024 20:11:03 +0200 Subject: [PATCH 30/56] refactor: IT better error handling --- .../integration-tests/src/local_fungibles.rs | 49 ++++++++++++++----- 1 file changed, 38 insertions(+), 11 deletions(-) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index b41508ab..399474de 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -353,8 +353,8 @@ fn token_metadata_works() { // }); // } -fn decoded(result: ExecReturnValue) -> T { - ::decode(&mut &result.data[1..]).expect("_") +fn decoded(result: ExecReturnValue) -> Result { + ::decode(&mut &result.data[1..]).map_err(|_| result) } // Call total_supply contract message. @@ -362,7 +362,10 @@ fn total_supply(addr: AccountId32, asset_id: AssetId) -> Result 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) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } // Call balance_of contract message. @@ -370,7 +373,10 @@ fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Resul 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) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } // Call allowance contract message. @@ -383,7 +389,10 @@ 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) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } // Call token_name contract message. @@ -391,7 +400,10 @@ fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { let function = function_selector("token_name"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::, Error>>(result) + match decoded::, Error>>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } // Call token_symbol contract message. @@ -399,7 +411,10 @@ fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> let function = function_selector("token_symbol"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::, Error>>(result) + match decoded::, Error>>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } // Call token_decimals contract message. @@ -407,7 +422,10 @@ fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { 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) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } fn transfer( @@ -419,7 +437,10 @@ fn transfer( let function = function_selector("transfer"); let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } fn approve( @@ -431,7 +452,10 @@ fn approve( let function = function_selector("approve"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } fn increase_allowance( @@ -443,7 +467,10 @@ fn increase_allowance( let function = function_selector("increase_allowance"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result) + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } } // fn asset_exists(addr: AccountId32, asset_id: AssetId) -> bool { From f72b15abbe11f031c1eb56e3668bcfffbce0019e Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 30 Jul 2024 12:25:23 +0200 Subject: [PATCH 31/56] feat: create & set_metadata --- pallets/api/src/fungibles/mod.rs | 37 +++ pallets/api/src/fungibles/tests.rs | 33 +- pop-api/examples/balance-transfer/lib.rs | 3 +- pop-api/examples/nfts/lib.rs | 49 +-- pop-api/examples/place-spot-order/lib.rs | 14 +- pop-api/examples/read-runtime-state/lib.rs | 55 +-- .../integration-tests/contracts/.gitignore | 9 + .../contracts/fungibles/lib.rs | 47 +-- .../integration-tests/src/local_fungibles.rs | 203 ++++++----- pop-api/src/v0/assets/fungibles.rs | 314 ++++++++++-------- runtime/devnet/src/config/api.rs | 6 +- 11 files changed, 451 insertions(+), 319 deletions(-) create mode 100755 pop-api/integration-tests/contracts/.gitignore diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 32f9af67..6cca9e90 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -177,6 +177,43 @@ pub mod pallet { let spender = T::Lookup::unlookup(spender); AssetsOf::::approve_transfer(origin, id.into(), spender, value) } + + /// Create a new token with a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `admin` - The account that will administer the asset. + /// * `min_balance` - The minimum balance required for accounts holding this asset. + #[pallet::call_index(11)] + #[pallet::weight(AssetsWeightInfoOf::::create())] + pub fn create( + origin: OriginFor, + id: AssetIdOf, + admin: AccountIdOf, + min_balance: BalanceOf, + ) -> DispatchResult { + let admin = T::Lookup::unlookup(admin); + AssetsOf::::create(origin, id.into(), admin, min_balance) + } + + /// Set the metadata for a token with a given asset ID. + /// + /// # Parameters + /// - `id`: The identifier of the asset to update. + /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. + /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. + /// - `decimals`: The number of decimals this asset uses to represent one unit. + #[pallet::call_index(16)] + #[pallet::weight(AssetsWeightInfoOf::::set_metadata(name.len() as u32, symbol.len() as u32))] + pub fn set_metadata( + origin: OriginFor, + id: AssetIdOf, + name: Vec, + symbol: Vec, + decimals: u8, + ) -> DispatchResult { + AssetsOf::::set_metadata(origin, id.into(), name, symbol, decimals) + } } impl Pallet { diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index c8de1020..3c548bb3 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -2,7 +2,9 @@ use crate::{fungibles::Read::*, mock::*}; use codec::Encode; use frame_support::{ assert_ok, - traits::fungibles::{approvals::Inspect, metadata::Inspect as MetadataInspect}, + traits::fungibles::{ + approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, Inspect, + }, }; const ASSET: u32 = 42; @@ -57,6 +59,35 @@ fn increase_allowance_works() { }); } +#[test] +fn create_works() { + new_test_ext().execute_with(|| { + assert!(!Assets::asset_exists(ASSET)); + assert_ok!(Fungibles::create(signed(ALICE), ASSET, ALICE, 100)); + assert!(Assets::asset_exists(ASSET)); + }); +} + +#[test] +fn set_metadata_works() { + new_test_ext().execute_with(|| { + let name = vec![42]; + let symbol = vec![42]; + let decimals = 42; + create_asset(ALICE, ASSET, 100); + assert_ok!(Fungibles::set_metadata( + signed(ALICE), + ASSET, + name.clone(), + symbol.clone(), + decimals + )); + assert_eq!(Assets::name(ASSET), name); + assert_eq!(Assets::symbol(ASSET), symbol); + assert_eq!(Assets::decimals(ASSET), decimals); + }); +} + #[test] fn total_supply_works() { new_test_ext().execute_with(|| { diff --git a/pop-api/examples/balance-transfer/lib.rs b/pop-api/examples/balance-transfer/lib.rs index 328a818b..d36dfa53 100755 --- a/pop-api/examples/balance-transfer/lib.rs +++ b/pop-api/examples/balance-transfer/lib.rs @@ -1,3 +1,4 @@ +// DEPRECATED #![cfg_attr(not(feature = "std"), no_std, no_main)] use pop_api::balances::*; @@ -131,4 +132,4 @@ mod balance_transfer { Ok(()) } } -} \ No newline at end of file +} diff --git a/pop-api/examples/nfts/lib.rs b/pop-api/examples/nfts/lib.rs index d47140e7..7920c179 100755 --- a/pop-api/examples/nfts/lib.rs +++ b/pop-api/examples/nfts/lib.rs @@ -1,3 +1,4 @@ +// DEPRECATED #![cfg_attr(not(feature = "std"), no_std, no_main)] use pop_api::nfts::*; @@ -33,27 +34,29 @@ mod nfts { } #[ink(message)] - pub fn create_nft_collection( &self ) -> Result<(), ContractError>{ + pub fn create_nft_collection(&self) -> Result<(), ContractError> { ink::env::debug_println!("Nfts::create_nft_collection: collection creation started."); - let admin = Self::env().caller(); - let item_settings = ItemSettings(BitFlags::from(ItemSetting::Transferable)); - - let mint_settings = MintSettings { - mint_type: MintType::Issuer, - price: Some(0), - start_block: Some(0), - end_block: Some(0), - default_item_settings: item_settings, - }; - - let config = CollectionConfig { - settings: CollectionSettings(BitFlags::from(CollectionSetting::TransferableItems)), - max_supply: None, - mint_settings, - }; - pop_api::nfts::create(admin, config)?; - ink::env::debug_println!("Nfts::create_nft_collection: collection created successfully."); - Ok(()) + let admin = Self::env().caller(); + let item_settings = ItemSettings(BitFlags::from(ItemSetting::Transferable)); + + let mint_settings = MintSettings { + mint_type: MintType::Issuer, + price: Some(0), + start_block: Some(0), + end_block: Some(0), + default_item_settings: item_settings, + }; + + let config = CollectionConfig { + settings: CollectionSettings(BitFlags::from(CollectionSetting::TransferableItems)), + max_supply: None, + mint_settings, + }; + pop_api::nfts::create(admin, config)?; + ink::env::debug_println!( + "Nfts::create_nft_collection: collection created successfully." + ); + Ok(()) } #[ink(message)] @@ -82,9 +85,7 @@ mod nfts { // check owner match owner(collection_id, item_id)? { Some(owner) if owner == receiver => { - ink::env::debug_println!( - "Nfts::mint success: minted item belongs to receiver" - ); + ink::env::debug_println!("Nfts::mint success: minted item belongs to receiver"); }, _ => { return Err(ContractError::NotOwner); @@ -113,4 +114,4 @@ mod nfts { Nfts::new(); } } -} \ No newline at end of file +} diff --git a/pop-api/examples/place-spot-order/lib.rs b/pop-api/examples/place-spot-order/lib.rs index f5e34f7f..669b9190 100755 --- a/pop-api/examples/place-spot-order/lib.rs +++ b/pop-api/examples/place-spot-order/lib.rs @@ -1,3 +1,4 @@ +// DEPRECATED #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract(env = pop_api::Environment)] @@ -15,11 +16,7 @@ mod spot_order { } #[ink(message)] - pub fn place_spot_order( - &mut self, - max_amount: Balance, - para_id: u32, - ) { + pub fn place_spot_order(&mut self, max_amount: Balance, para_id: u32) { ink::env::debug_println!( "SpotOrder::place_spot_order: max_amount {:?} para_id: {:?} ", max_amount, @@ -28,10 +25,7 @@ mod spot_order { #[allow(unused_variables)] let res = pop_api::cross_chain::coretime::place_spot_order(max_amount, para_id); - ink::env::debug_println!( - "SpotOrder::place_spot_order: res {:?} ", - res, - ); + ink::env::debug_println!("SpotOrder::place_spot_order: res {:?} ", res,); ink::env::debug_println!("SpotOrder::place_spot_order end"); } @@ -46,4 +40,4 @@ mod spot_order { SpotOrder::new(); } } -} \ No newline at end of file +} diff --git a/pop-api/examples/read-runtime-state/lib.rs b/pop-api/examples/read-runtime-state/lib.rs index 05a44108..60ef70f0 100755 --- a/pop-api/examples/read-runtime-state/lib.rs +++ b/pop-api/examples/read-runtime-state/lib.rs @@ -1,35 +1,36 @@ +// DEPRECATED #![cfg_attr(not(feature = "std"), no_std, no_main)] #[ink::contract(env = pop_api::Environment)] mod read_relay_blocknumber { - use pop_api::primitives::storage_keys::{ - ParachainSystemKeys::LastRelayChainBlockNumber, RuntimeStateKeys::ParachainSystem, - }; + use pop_api::primitives::storage_keys::{ + ParachainSystemKeys::LastRelayChainBlockNumber, RuntimeStateKeys::ParachainSystem, + }; - #[ink(event)] - pub struct RelayBlockNumberRead { - value: BlockNumber, - } + #[ink(event)] + pub struct RelayBlockNumberRead { + value: BlockNumber, + } - #[ink(storage)] - #[derive(Default)] - pub struct ReadRelayBlockNumber; + #[ink(storage)] + #[derive(Default)] + pub struct ReadRelayBlockNumber; - impl ReadRelayBlockNumber { - #[ink(constructor, payable)] - pub fn new() -> Self { - ink::env::debug_println!("ReadRelayBlockNumber::new"); - Default::default() - } + impl ReadRelayBlockNumber { + #[ink(constructor, payable)] + pub fn new() -> Self { + ink::env::debug_println!("ReadRelayBlockNumber::new"); + Default::default() + } - #[ink(message)] - pub fn read_relay_block_number(&self) { - let result = - pop_api::state::read::(ParachainSystem(LastRelayChainBlockNumber)); - ink::env::debug_println!("Last relay block number read by contract: {:?}", result); - self.env().emit_event(RelayBlockNumberRead { - value: result.expect("Failed to read relay block number."), - }); - } - } -} \ No newline at end of file + #[ink(message)] + pub fn read_relay_block_number(&self) { + let result = + pop_api::state::read::(ParachainSystem(LastRelayChainBlockNumber)); + ink::env::debug_println!("Last relay block number read by contract: {:?}", result); + self.env().emit_event(RelayBlockNumberRead { + value: result.expect("Failed to read relay block number."), + }); + } + } +} diff --git a/pop-api/integration-tests/contracts/.gitignore b/pop-api/integration-tests/contracts/.gitignore new file mode 100755 index 00000000..d60800c8 --- /dev/null +++ b/pop-api/integration-tests/contracts/.gitignore @@ -0,0 +1,9 @@ +# Ignore build artifacts from the local tests sub-crate. +**/target/ + +# Ignore backup files creates by cargo fmt. +**/*.rs.bk + +# Remove Cargo.lock when creating an executable, leave it for libraries +# More information here http://doc.crates.io/guide.html#cargotoml-vs-cargolock +**/Cargo.lock diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs index 1b42fec4..234dca2f 100755 --- a/pop-api/integration-tests/contracts/fungibles/lib.rs +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -131,40 +131,21 @@ mod fungibles { // - 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 create(&self, id: AssetId, admin: AccountId, min_balance: Balance) -> Result<()> { + api::create(id, admin, min_balance) + } - // #[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 set_metadata( + &self, + id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, + ) -> Result<()> { + api::set_metadata(id, name, symbol, decimals) + } // // #[ink(message)] // pub fn asset_exists(&self, id: AssetId) -> Result { diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 6de57ca4..6ef2b378 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -109,29 +109,31 @@ fn increase_allowance( // decoded::(result) // } // -// fn create( -// addr: AccountId32, -// asset_id: AssetId, -// admin: AccountId32, -// min_balance: Balance, -// ) -> ExecReturnValue { -// let function = function_selector("create"); -// let params = [function, asset_id.encode(), admin.encode(), min_balance.encode()].concat(); -// bare_call(addr, params, 0).expect("should work") -// } -// -// fn set_metadata( -// addr: AccountId32, -// asset_id: AssetId, -// name: Vec, -// symbol: Vec, -// decimals: u8, -// ) -> ExecReturnValue { -// let function = function_selector("set_metadata"); -// let params = -// [function, asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); -// bare_call(addr, params, 0).expect("should work") -// } +fn create( + addr: AccountId32, + asset_id: AssetId, + admin: AccountId32, + min_balance: Balance, +) -> ExecReturnValue { + let function = function_selector("create"); + let params = [function, asset_id.encode(), admin.encode(), min_balance.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} + +fn set_metadata( + addr: AccountId32, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) -> ExecReturnValue { + let function = function_selector("set_metadata"); + let params = + [function, asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} fn create_asset(owner: AccountId32, asset_id: AssetId, min_balance: Balance) -> AssetId { assert_ok!(Assets::create( @@ -496,6 +498,108 @@ fn token_metadata_works() { }); } +/// 3. Asset Management: +/// - create +/// - start_destroy +/// - destroy_accounts +/// - destroy_approvals +/// - finish_destroy +/// - set_metadata + +#[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!( + decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), + Ok(Module { index: 10, error: 2 }), + ); + // Instantiate a contract without balance for deposit. + let addr = instantiate(CONTRACT, 100, vec![1]); + // No balance to pay the deposit. + assert_eq!( + decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), + Ok(Module { index: 10, error: 2 }), + ); + // Instantiate a contract with enough balance. + let addr = instantiate(CONTRACT, INIT_VALUE, vec![2]); + assert_eq!( + decoded::(create(addr.clone(), ASSET_ID, BOB, 0)), + Ok(Module { index: 52, error: 7 }), + ); + // The minimal balance for an asset must be non zero. + assert_eq!( + decoded::(create(addr.clone(), ASSET_ID, BOB, 0)), + Ok(Module { index: 52, error: 7 }), + ); + // Create asset successfully. + let result = create(addr.clone(), ASSET_ID, BOB, 1); + assert!(!result.did_revert(), "Contract reverted!"); + // Asset ID is already taken. + assert_eq!( + decoded::(create(addr.clone(), ASSET_ID, BOB, 1)), + Ok(Module { index: 52, error: 5 }), + ); + }); +} + +#[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. + assert_eq!( + decoded::(set_metadata(addr.clone(), ASSET_ID, vec![0], vec![0], 0u8)), + Ok(Module { index: 52, error: 3 }), + ); + // Create assets where contract is not the owner. + let asset = create_asset(ALICE, 0, 1); + // No Permission. + assert_eq!( + decoded::(set_metadata(addr.clone(), asset, vec![0], vec![0], 0u8)), + Ok(Module { index: 52, error: 2 }), + ); + let asset = create_asset(addr.clone(), ASSET_ID, 1); + // Asset is not live, i.e. frozen or being destroyed. + freeze_asset(addr.clone(), asset); + assert_eq!( + decoded::(set_metadata(addr.clone(), ASSET_ID, vec![0], vec![0], 0u8)), + Ok(Module { index: 52, error: 16 }), + ); + thaw_asset(addr.clone(), asset); + // 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!( + decoded::(set_metadata( + addr.clone(), + ASSET_ID, + vec![0; 1000], + vec![0; 1000], + 0u8 + )), + Ok(Module { index: 52, error: 9 }), + ); + // Set metadata successfully. + let result = set_metadata(addr.clone(), ASSET_ID, name, symbol, decimals); + assert!(!result.did_revert(), "Contract reverted!"); + // Asset is not live, i.e. frozen or being destroyed. + start_destroy_asset(addr.clone(), asset); + assert_eq!( + decoded::(set_metadata(addr.clone(), ASSET_ID, vec![0], vec![0], decimals)), + Ok(Module { index: 52, error: 16 }), + ); + }); +} + // #[test] // #[ignore] // fn asset_exists_works() { @@ -572,56 +676,3 @@ fn token_metadata_works() { // ); // }); // } - -// #[test] -// #[ignore] -// fn create_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// // Instantiate a contract without balance (relay token). -// let addr = instantiate(CONTRACT, 0, vec![0]); -// // No balance to pay for fees. -// assert_eq!( -// decoded::(create(addr.clone(), ASSET_ID, addr.clone(), 1)), -// 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)), -// 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)), -// 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)), -// Ok(Module { index: 52, error: 5 }), -// ); -// // The minimal balance for an asset must be non zero. -// let new_asset = 2; -// let result = create(addr.clone(), new_asset, BOB, 1); -// assert!(!result.did_revert(), "Contract reverted!"); -// }); -// } - -// #[test] -// #[ignore] -// fn set_metadata_works() { -// new_test_ext().execute_with(|| { -// let _ = env_logger::try_init(); -// let addr = -// instantiate(CONTRACT, INIT_VALUE, vec![]); -// -// create_asset(addr.clone(), ASSET_ID, 1); -// let result = set_metadata(addr.clone(), ASSET_ID, vec![12], vec![12], 12); -// assert!(!result.did_revert(), "Contract reverted!"); -// }); -// } diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 0712a80b..23eff2b6 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -3,6 +3,7 @@ use crate::{ primitives::{AccountId, AssetId, Balance}, Result, StatusCode, }; +pub use asset_management::*; use constants::*; use ink::{env::chain_extension::ChainExtensionMethod, prelude::vec::Vec}; pub use metadata::*; @@ -30,45 +31,34 @@ fn build_read_state(state_query: u8) -> ChainExtensionMethod<(), (), (), false> mod constants { /// 1. PSP-22 Interface: - /// - total_supply - pub const TOTAL_SUPPLY: u8 = 0; - /// - balance_of - pub const BALANCE_OF: u8 = 1; - /// - allowance - pub const ALLOWANCE: u8 = 2; - /// - transfer + pub(super) const TOTAL_SUPPLY: u8 = 0; + pub(super) const BALANCE_OF: u8 = 1; + pub(super) const ALLOWANCE: u8 = 2; pub(super) const TRANSFER: u8 = 3; - /// - transfer_from pub(super) const TRANSFER_FROM: u8 = 4; - /// - approve pub(super) const APPROVE: u8 = 5; - /// - increase_allowance pub(super) const INCREASE_ALLOWANCE: u8 = 6; - /// - decrease_allowance pub(super) const DECREASE_ALLOWANCE: u8 = 7; /// 2. PSP-22 Metadata Interface: - /// - token_name - pub const TOKEN_NAME: u8 = 8; - /// - token_symbol - pub const TOKEN_SYMBOL: u8 = 9; - /// - token_decimals - pub const TOKEN_DECIMALS: u8 = 10; - - // 3. Asset Management: - // - create - // - start_destroy - // - destroy_accounts - // - destroy_approvals - // - finish_destroy - // - set_metadata - // - clear_metadata + pub(super) const TOKEN_NAME: u8 = 8; + pub(super) const TOKEN_SYMBOL: u8 = 9; + pub(super) const TOKEN_DECIMALS: u8 = 10; + + /// 3. Asset Management: + pub(super) const CREATE: u8 = 11; + pub(super) const START_DESTROY: u8 = 12; + pub(super) const DESTROY_ACCOUNTS: u8 = 13; + pub(super) const DESTROY_APPROVALS: u8 = 14; + pub(super) const FINISH_DESTROY: u8 = 15; + pub(super) const SET_METADATA: u8 = 16; + pub(super) const CLEAR_METADATA: u8 = 17; } /// Returns the total token supply for a given asset ID. /// -/// # Arguments -/// * `id` - The ID of the asset. +/// # Parameters +/// - `id` - The ID of the asset. /// /// # Returns /// The total supply of the token, or an error if the operation fails. @@ -84,9 +74,9 @@ pub fn total_supply(id: AssetId) -> Result { /// Returns the account balance for the specified `owner` for a given asset ID. Returns `0` if /// the account is non-existent. /// -/// # Arguments -/// * `id` - The ID of the asset. -/// * `owner` - The account whose balance is being queried. +/// # Parameters +/// - `id` - The ID of the asset. +/// - `owner` - The account whose balance is being queried. /// /// # Returns /// The balance of the specified account, or an error if the operation fails. @@ -102,10 +92,10 @@ pub fn balance_of(id: AssetId, owner: AccountId) -> Result { /// Returns the amount which `spender` is still allowed to withdraw from `owner` for a given /// asset ID. Returns `0` if no allowance has been set. /// -/// # Arguments -/// * `id` - The ID of the asset. -/// * `owner` - The account that owns the tokens. -/// * `spender` - The account that is allowed to spend the tokens. +/// # Parameters +/// - `id` - The ID of the asset. +/// - `owner` - The account that owns the tokens. +/// - `spender` - The account that is allowed to spend the tokens. /// /// # Returns /// The remaining allowance, or an error if the operation fails. @@ -121,10 +111,10 @@ pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result Result<()> { /// in unspecified format. If `from` is equal to `None`, tokens will be minted to account `to`. If /// `to` is equal to `None`, tokens will be burned from account `from`. /// -/// # Arguments -/// * `id` - The ID of the asset. -/// * `from` - The account from which the tokens are transferred. -/// * `to` - The recipient account. -/// * `value` - The number of tokens to transfer. +/// # Parameters +/// - `id` - The ID of the asset. +/// - `from` - The account from which the tokens are transferred. +/// - `to` - The recipient account. +/// - `value` - The number of tokens to transfer. /// /// # Returns /// Returns `Ok(())` if successful, or an error if the transfer fails. @@ -160,10 +150,10 @@ pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balanc /// Approves an account to spend a specified number of tokens on behalf of the caller. /// -/// # Arguments -/// * `id` - The ID of the asset. -/// * `spender` - The account that is allowed to spend the tokens. -/// * `value` - The number of tokens to approve. +/// # Parameters +/// - `id` - The ID of the asset. +/// - `spender` - The account that is allowed to spend the tokens. +/// - `value` - The number of tokens to approve. /// /// # Returns /// Returns `Ok(())` if successful, or an error if the approval fails. @@ -178,10 +168,10 @@ pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { /// Increases the allowance of a spender. /// -/// # Arguments -/// * `id` - The ID of the asset. -/// * `spender` - The account that is allowed to spend the tokens. -/// * `value` - The number of tokens to increase the allowance by. +/// # Parameters +/// - `id` - The ID of the asset. +/// - `spender` - The account that is allowed to spend the tokens. +/// - `value` - The number of tokens to increase the allowance by. /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. @@ -196,10 +186,10 @@ pub fn increase_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re /// Decreases the allowance of a spender. /// -/// # Arguments -/// * `id` - The ID of the asset. -/// * `spender` - The account that is allowed to spend the tokens. -/// * `value` - The number of tokens to decrease the allowance by. +/// # Parameters +/// - `id` - The ID of the asset. +/// - `spender` - The account that is allowed to spend the tokens. +/// - `value` - The number of tokens to decrease the allowance by. /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. @@ -216,8 +206,8 @@ pub mod metadata { use super::*; /// Returns the token name for a given asset ID. /// - /// # Arguments - /// * `id` - The ID of the asset. + /// # Parameters + /// - `id` - The ID of the asset. /// /// # Returns /// The name of the token as a byte vector, or an error if the operation fails. @@ -232,8 +222,8 @@ pub mod metadata { /// Returns the token symbol for a given asset ID. /// - /// # Arguments - /// * `id` - The ID of the asset. + /// # Parameters + /// - `id` - The ID of the asset. /// /// # Returns /// The symbol of the token as a byte vector, or an error if the operation fails. @@ -248,8 +238,8 @@ pub mod metadata { /// Returns the token decimals for a given asset ID. /// - /// # Arguments - /// * `id` - The ID of the asset. + /// # Parameters + /// - `id` - The ID of the asset. /// /// # Returns /// The number of decimals of the token as a byte vector, or an error if the operation fails. @@ -263,90 +253,122 @@ pub mod metadata { } } -// pub asset_management { -// /// Create a new token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// * `admin` - The account that will administer the asset. -// /// * `min_balance` - The minimum balance required for accounts holding this asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the creation fails. -// pub fn create(id: AssetId, admin: impl Into>, min_balance: Balance) -> Result<()> { -// Ok(()) -// } -// -// /// Start the process of destroying a token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the operation fails. -// fn start_destroy(id: AssetId) -> Result<()> { -// Ok(()) -// } -// -// /// Destroy all accounts associated with a token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the operation fails. -// fn destroy_accounts(id: AssetId) -> Result<()> { -// Ok(()) -// } -// -// /// Destroy all approvals associated with a token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the operation fails. -// fn destroy_approvals(id: AssetId) -> Result<()> { -// Ok(()) -// } -// -// /// Complete the process of destroying a token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the operation fails. -// fn finish_destroy(id: AssetId) -> Result<()> { -// Ok(()) -// } -// -// /// Set the metadata for a token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the operation fails. -// pub fn set_metadata(id: AssetId, name: Vec, symbol: Vec, decimals: u8) -> Result<()> { -// Ok(()) -// } -// -// /// Clear the metadata for a token with a given asset ID. -// /// -// /// # Arguments -// /// * `id` - The ID of the asset. -// /// -// /// # Returns -// /// Returns `Ok(())` if successful, or an error if the operation fails. -// fn clear_metadata(id: AssetId) -> Result<()> { -// Ok(()) -// } -// } -// -// pub fn asset_exists(id: AssetId) -> Result { -// assets::asset_exists(id) -// } +pub mod asset_management { + use super::*; + /// Create a new token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// - `admin` - The account that will administer the asset. + /// - `min_balance` - The minimum balance required for accounts holding this asset. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the creation fails. + pub fn create(id: AssetId, admin: AccountId, min_balance: Balance) -> Result<()> { + build_dispatch(CREATE) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, admin, min_balance)) + } + + /// Start the process of destroying a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. + fn start_destroy(id: AssetId) -> Result<()> { + build_dispatch(START_DESTROY) + .input::() + .output::, true>() + .handle_error_code::() + .call(&(id)) + } + + /// Destroy all accounts associated with a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. + fn destroy_accounts(id: AssetId) -> Result<()> { + build_dispatch(DESTROY_ACCOUNTS) + .input::() + .output::, true>() + .handle_error_code::() + .call(&(id)) + } + + /// Destroy all approvals associated with a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. + fn destroy_approvals(id: AssetId) -> Result<()> { + build_dispatch(DESTROY_APPROVALS) + .input::() + .output::, true>() + .handle_error_code::() + .call(&(id)) + } + + /// Complete the process of destroying a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. + fn finish_destroy(id: AssetId) -> Result<()> { + build_dispatch(FINISH_DESTROY) + .input::() + .output::, true>() + .handle_error_code::() + .call(&(id)) + } + + /// Set the metadata for a token with a given asset ID. + /// + /// # Parameters + /// - `id`: The identifier of the asset to update. + /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. + /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. + /// - `decimals`: The number of decimals this asset uses to represent one unit. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. + pub fn set_metadata(id: AssetId, name: Vec, symbol: Vec, decimals: u8) -> Result<()> { + build_dispatch(SET_METADATA) + .input::<(AssetId, Vec, Vec, u8)>() + .output::, true>() + .handle_error_code::() + .call(&(id, name, symbol, decimals)) + } + + /// Clear the metadata for a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// + /// # Returns + /// Returns `Ok(())` if successful, or an error if the operation fails. + fn clear_metadata(id: AssetId) -> Result<()> { + build_dispatch(CLEAR_METADATA) + .input::() + .output::, true>() + .handle_error_code::() + .call(&(id)) + } + // + // pub fn asset_exists(id: AssetId) -> Result { + // assets::asset_exists(id) + // } +} /// Represents various errors related to local fungible assets in the Pop API. /// diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 884128f2..72204a8c 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -20,7 +20,11 @@ impl Contains for AllowedApiCalls { use fungibles::Call::*; matches!( c, - RuntimeCall::Fungibles(transfer { .. } | approve { .. } | increase_allowance { .. }) + RuntimeCall::Fungibles( + transfer { .. } + | approve { .. } | increase_allowance { .. } + | create { .. } | set_metadata { .. } + ) ) } } From 7739392b42730187e91743bf7bb1f7db8b877202 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 30 Jul 2024 17:04:09 +0200 Subject: [PATCH 32/56] feat: rest of asset management --- pallets/api/src/fungibles/mod.rs | 63 ++++++- pallets/api/src/fungibles/tests.rs | 25 +++ .../contracts/fungibles/lib.rs | 27 ++- .../integration-tests/src/local_fungibles.rs | 174 +++++++++++++++++- pop-api/src/v0/assets/fungibles.rs | 10 +- runtime/devnet/src/config/api.rs | 5 + 6 files changed, 295 insertions(+), 9 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 6cca9e90..c0561c4b 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -21,6 +21,7 @@ type AssetIdParameterOf = >>:: type AssetsOf = pallet_assets::Pallet>; type AssetsInstanceOf = ::AssetsInstance; type AssetsWeightInfoOf = >>::WeightInfo; +type RemoveItemsLimitOf = >>::RemoveItemsLimit; type BalanceOf = > as Inspect< ::AccountId, >>::Balance; @@ -196,12 +197,64 @@ pub mod pallet { AssetsOf::::create(origin, id.into(), admin, min_balance) } + /// Create a new token with a given asset ID. + /// + /// # Arguments + /// * `id` - The ID of the asset. + /// * `admin` - The account that will administer the asset. + /// * `min_balance` - The minimum balance required for accounts holding this asset. + #[pallet::call_index(12)] + #[pallet::weight(AssetsWeightInfoOf::::start_destroy())] + pub fn start_destroy(origin: OriginFor, id: AssetIdOf) -> DispatchResult { + AssetsOf::::start_destroy(origin, id.into()) + } + + /// Destroy all accounts associated with a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + // TODO: weight function + #[pallet::call_index(13)] + #[pallet::weight(AssetsWeightInfoOf::::destroy_accounts(RemoveItemsLimitOf::::get() / 6))] + pub fn destroy_accounts( + origin: OriginFor, + id: AssetIdOf, + ) -> DispatchResultWithPostInfo { + AssetsOf::::destroy_accounts(origin, id.into()) + } + + /// Destroy all approvals associated with a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + // TODO: weight function + #[pallet::call_index(14)] + #[pallet::weight(AssetsWeightInfoOf::::destroy_approvals(RemoveItemsLimitOf::::get() / 4))] + pub fn destroy_approvals( + origin: OriginFor, + id: AssetIdOf, + ) -> DispatchResultWithPostInfo { + AssetsOf::::destroy_approvals(origin, id.into()) + } + + /// Complete the process of destroying a token with a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + #[pallet::call_index(15)] + #[pallet::weight(AssetsWeightInfoOf::::finish_destroy())] + pub fn finish_destroy(origin: OriginFor, id: AssetIdOf) -> DispatchResult { + AssetsOf::::finish_destroy(origin, id.into()) + } + /// Set the metadata for a token with a given asset ID. /// /// # Parameters /// - `id`: The identifier of the asset to update. - /// - `name`: The user friendly name of this asset. Limited in length by `StringLimit`. - /// - `symbol`: The exchange symbol for this asset. Limited in length by `StringLimit`. + /// - `name`: The user friendly name of this asset. Limited in length by + /// `pallet_assets::Config::StringLimit`. + /// - `symbol`: The exchange symbol for this asset. Limited in length by + /// `pallet_assets::Config::StringLimit`. /// - `decimals`: The number of decimals this asset uses to represent one unit. #[pallet::call_index(16)] #[pallet::weight(AssetsWeightInfoOf::::set_metadata(name.len() as u32, symbol.len() as u32))] @@ -214,6 +267,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::set_metadata(origin, id.into(), name, symbol, decimals) } + + #[pallet::call_index(17)] + #[pallet::weight(AssetsWeightInfoOf::::clear_metadata())] + pub fn clear_metadata(origin: OriginFor, id: AssetIdOf) -> DispatchResult { + AssetsOf::::clear_metadata(origin, id.into()) + } } impl Pallet { diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 3c548bb3..735aadf1 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -68,6 +68,17 @@ fn create_works() { }); } +#[test] +fn destroy_asset_works() { + new_test_ext().execute_with(|| { + create_asset(ALICE, ASSET, 100); + assert_ok!(Fungibles::start_destroy(signed(ALICE), ASSET)); + assert_ok!(Fungibles::destroy_accounts(signed(ALICE), ASSET)); + assert_ok!(Fungibles::destroy_approvals(signed(ALICE), ASSET)); + assert_ok!(Fungibles::finish_destroy(signed(ALICE), ASSET)); + }); +} + #[test] fn set_metadata_works() { new_test_ext().execute_with(|| { @@ -88,6 +99,20 @@ fn set_metadata_works() { }); } +#[test] +fn clear_metadata_works() { + new_test_ext().execute_with(|| { + let name = vec![42]; + let symbol = vec![42]; + let decimals = 42; + create_asset_and_set_metadata(ALICE, ASSET, name, symbol, decimals); + assert_ok!(Fungibles::clear_metadata(signed(ALICE), ASSET)); + assert_eq!(Assets::name(ASSET), Vec::::new()); + assert_eq!(Assets::symbol(ASSET), Vec::::new()); + assert_eq!(Assets::decimals(ASSET), 0u8); + }); +} + #[test] fn total_supply_works() { new_test_ext().execute_with(|| { diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs index 234dca2f..105d5564 100755 --- a/pop-api/integration-tests/contracts/fungibles/lib.rs +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -136,6 +136,26 @@ mod fungibles { api::create(id, admin, min_balance) } + #[ink(message)] + pub fn start_destroy(&self, id: AssetId) -> Result<()> { + api::start_destroy(id) + } + + #[ink(message)] + pub fn destroy_accounts(&self, id: AssetId) -> Result<()> { + api::destroy_accounts(id) + } + + #[ink(message)] + pub fn destroy_approvals(&self, id: AssetId) -> Result<()> { + api::destroy_approvals(id) + } + + #[ink(message)] + pub fn finish_destroy(&self, id: AssetId) -> Result<()> { + api::finish_destroy(id) + } + #[ink(message)] pub fn set_metadata( &self, @@ -146,7 +166,12 @@ mod fungibles { ) -> Result<()> { api::set_metadata(id, name, symbol, decimals) } - // + + #[ink(message)] + pub fn clear_metadata(&self, id: AssetId) -> Result<()> { + api::clear_metadata(id) + } + // #[ink(message)] // pub fn asset_exists(&self, id: AssetId) -> Result { // // api::asset_exists(id).map_err(|e| e.into()) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 6ef2b378..33d43c40 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -121,6 +121,34 @@ fn create( result } +fn start_destroy(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { + let function = function_selector("start_destroy"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} + +fn destroy_accounts(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { + let function = function_selector("destroy_accounts"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} + +fn destroy_approvals(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { + let function = function_selector("destroy_approvals"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} + +fn finish_destroy(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { + let function = function_selector("finish_destroy"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} + fn set_metadata( addr: AccountId32, asset_id: AssetId, @@ -135,6 +163,13 @@ fn set_metadata( result } +fn clear_metadata(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { + let function = function_selector("clear_metadata"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + result +} + fn create_asset(owner: AccountId32, asset_id: AssetId, min_balance: Balance) -> AssetId { assert_ok!(Assets::create( RuntimeOrigin::signed(owner.clone()), @@ -205,7 +240,7 @@ fn create_asset_and_set_metadata( name: Vec, symbol: Vec, decimals: u8, -) { +) -> AssetId { assert_ok!(Assets::create( RuntimeOrigin::signed(owner.clone()), asset_id.into(), @@ -213,6 +248,7 @@ fn create_asset_and_set_metadata( 100 )); set_metadata_asset(owner, asset_id, name, symbol, decimals); + asset_id } // Set metadata of an asset. @@ -546,6 +582,95 @@ fn create_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!( + decoded::(start_destroy(addr.clone(), ASSET_ID)), + Ok(Module { index: 52, error: 3 }), + ); + // Create assets where contract is not the owner. + let asset = create_asset(ALICE, 0, 1); + // No Permission. + assert_eq!( + decoded::(start_destroy(addr.clone(), asset)), + Ok(Module { index: 52, error: 2 }), + ); + let asset = create_asset(addr.clone(), ASSET_ID, 1); + let result = start_destroy(addr.clone(), asset); + assert!(!result.did_revert(), "Contract reverted!"); + }); +} + +#[test] +fn destroy_accounts_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!( + decoded::(destroy_accounts(addr.clone(), ASSET_ID)), + Ok(Module { index: 52, error: 3 }), + ); + let asset = create_asset(addr.clone(), ASSET_ID, 1); + // The destroy process hasn't been started. + assert_eq!( + decoded::(destroy_accounts(addr.clone(), asset)), + Ok(Module { index: 52, error: 17 }), + ); + start_destroy_asset(addr.clone(), asset); + let result = destroy_accounts(addr.clone(), asset); + assert!(!result.did_revert(), "Contract reverted!"); + }); +} + +#[test] +fn destroy_approvals_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!( + decoded::(destroy_approvals(addr.clone(), ASSET_ID)), + Ok(Module { index: 52, error: 3 }), + ); + let asset = create_asset(addr.clone(), ASSET_ID, 1); + // The destroy process hasn't been started. + assert_eq!( + decoded::(destroy_approvals(addr.clone(), asset)), + Ok(Module { index: 52, error: 17 }), + ); + start_destroy_asset(addr.clone(), asset); + let result = destroy_approvals(addr.clone(), asset); + assert!(!result.did_revert(), "Contract reverted!"); + }); +} + +#[test] +fn finish_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!( + decoded::(finish_destroy(addr.clone(), ASSET_ID)), + Ok(Module { index: 52, error: 3 }), + ); + let asset = create_asset(addr.clone(), ASSET_ID, 1); + // The destroy process hasn't been started. + assert_eq!( + decoded::(finish_destroy(addr.clone(), asset)), + Ok(Module { index: 52, error: 17 }), + ); + start_destroy_asset(addr.clone(), asset); + let result = finish_destroy(addr.clone(), asset); + assert!(!result.did_revert(), "Contract reverted!"); + }); +} + #[test] fn set_metadata_works() { new_test_ext().execute_with(|| { @@ -593,6 +718,53 @@ fn set_metadata_works() { assert!(!result.did_revert(), "Contract reverted!"); // Asset is not live, i.e. frozen or being destroyed. start_destroy_asset(addr.clone(), asset); + assert_eq!( + decoded::(set_metadata(addr.clone(), ASSET_ID, vec![0], vec![0], 0)), + Ok(Module { index: 52, error: 16 }), + ); + }); +} + +#[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!( + decoded::(clear_metadata(addr.clone(), 0)), + Ok(Module { index: 52, error: 3 }), + ); + // Create assets where contract is not the owner. + let asset = create_asset_and_set_metadata(ALICE, 0, vec![0], vec![0], 0); + // No Permission. + assert_eq!( + decoded::(clear_metadata(addr.clone(), asset)), + Ok(Module { index: 52, error: 2 }), + ); + let asset = create_asset(addr.clone(), ASSET_ID, 1); + // Asset is not live, i.e. frozen or being destroyed. + freeze_asset(addr.clone(), asset); + assert_eq!( + decoded::(clear_metadata(addr.clone(), asset)), + Ok(Module { index: 52, error: 16 }), + ); + thaw_asset(addr.clone(), asset); + // No metadata set. + assert_eq!( + decoded::(clear_metadata(addr.clone(), asset)), + Ok(Module { index: 52, error: 3 }), + ); + set_metadata_asset(addr.clone(), asset, name, symbol, decimals); + // Clear metadata successfully. + let result = clear_metadata(addr.clone(), ASSET_ID); + assert!(!result.did_revert(), "Contract reverted!"); + // Asset is not live, i.e. frozen or being destroyed. + start_destroy_asset(addr.clone(), asset); assert_eq!( decoded::(set_metadata(addr.clone(), ASSET_ID, vec![0], vec![0], decimals)), Ok(Module { index: 52, error: 16 }), diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 23eff2b6..ec2b9059 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -279,7 +279,7 @@ pub mod asset_management { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. - fn start_destroy(id: AssetId) -> Result<()> { + pub fn start_destroy(id: AssetId) -> Result<()> { build_dispatch(START_DESTROY) .input::() .output::, true>() @@ -294,7 +294,7 @@ pub mod asset_management { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. - fn destroy_accounts(id: AssetId) -> Result<()> { + pub fn destroy_accounts(id: AssetId) -> Result<()> { build_dispatch(DESTROY_ACCOUNTS) .input::() .output::, true>() @@ -309,7 +309,7 @@ pub mod asset_management { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. - fn destroy_approvals(id: AssetId) -> Result<()> { + pub fn destroy_approvals(id: AssetId) -> Result<()> { build_dispatch(DESTROY_APPROVALS) .input::() .output::, true>() @@ -324,7 +324,7 @@ pub mod asset_management { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. - fn finish_destroy(id: AssetId) -> Result<()> { + pub fn finish_destroy(id: AssetId) -> Result<()> { build_dispatch(FINISH_DESTROY) .input::() .output::, true>() @@ -357,7 +357,7 @@ pub mod asset_management { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. - fn clear_metadata(id: AssetId) -> Result<()> { + pub fn clear_metadata(id: AssetId) -> Result<()> { build_dispatch(CLEAR_METADATA) .input::() .output::, true>() diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 72204a8c..bc38a3c3 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -24,6 +24,11 @@ impl Contains for AllowedApiCalls { transfer { .. } | approve { .. } | increase_allowance { .. } | create { .. } | set_metadata { .. } + | start_destroy { .. } + | destroy_accounts { .. } + | destroy_approvals { .. } + | finish_destroy { .. } + | clear_metadata { .. } ) ) } From d4f7f99445509f560f3cab304fab3be6300072e0 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 30 Jul 2024 17:15:32 +0200 Subject: [PATCH 33/56] style: pub function docs --- pallets/api/src/fungibles/mod.rs | 36 ++++++++++++++++---------------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index c0561c4b..9579a66c 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -96,9 +96,9 @@ pub mod pallet { /// `data` in unspecified format. /// /// # Parameters - /// * `id` - The ID of the asset. - /// * `to` - The recipient account. - /// * `value` - The number of tokens to transfer. + /// - `id` - The ID of the asset. + /// - `to` - The recipient account. + /// - `value` - The number of tokens to transfer. #[pallet::call_index(3)] #[pallet::weight(AssetsWeightInfoOf::::transfer_keep_alive())] pub fn transfer( @@ -114,9 +114,9 @@ pub mod pallet { /// Approves an account to spend a specified number of tokens on behalf of the caller. /// /// # Parameters - /// * `id` - The ID of the asset. - /// * `spender` - The account that is allowed to spend the tokens. - /// * `value` - The number of tokens to approve. + /// - `id` - The ID of the asset. + /// - `spender` - The account that is allowed to spend the tokens. + /// - `value` - The number of tokens to approve. #[pallet::call_index(5)] #[pallet::weight(::WeightInfo::approve(1, 1))] pub fn approve( @@ -164,9 +164,9 @@ pub mod pallet { /// Increases the allowance of a spender. /// /// # Parameters - /// * `id` - The ID of the asset. - /// * `spender` - The account that is allowed to spend the tokens. - /// * `value` - The number of tokens to increase the allowance by. + /// - `id` - The ID of the asset. + /// - `spender` - The account that is allowed to spend the tokens. + /// - `value` - The number of tokens to increase the allowance by. #[pallet::call_index(6)] #[pallet::weight(AssetsWeightInfoOf::::approve_transfer())] pub fn increase_allowance( @@ -181,10 +181,10 @@ pub mod pallet { /// Create a new token with a given asset ID. /// - /// # Arguments - /// * `id` - The ID of the asset. - /// * `admin` - The account that will administer the asset. - /// * `min_balance` - The minimum balance required for accounts holding this asset. + /// # Parameters + /// - `id` - The ID of the asset. + /// - `admin` - The account that will administer the asset. + /// - `min_balance` - The minimum balance required for accounts holding this asset. #[pallet::call_index(11)] #[pallet::weight(AssetsWeightInfoOf::::create())] pub fn create( @@ -199,10 +199,10 @@ pub mod pallet { /// Create a new token with a given asset ID. /// - /// # Arguments - /// * `id` - The ID of the asset. - /// * `admin` - The account that will administer the asset. - /// * `min_balance` - The minimum balance required for accounts holding this asset. + /// # Parameters + /// - `id` - The ID of the asset. + /// - `admin` - The account that will administer the asset. + /// - `min_balance` - The minimum balance required for accounts holding this asset. #[pallet::call_index(12)] #[pallet::weight(AssetsWeightInfoOf::::start_destroy())] pub fn start_destroy(origin: OriginFor, id: AssetIdOf) -> DispatchResult { @@ -282,7 +282,7 @@ pub mod pallet { /// encoded result. /// /// # Parameter - /// * `value` - An instance of `Read`, which specifies the type of state query and + /// - `value` - An instance of `Read`, which specifies the type of state query and /// the associated parameters. pub fn read_state(value: Read) -> Vec { use Read::*; From 6f4da1852fd7923a1616cc2481ecb3c731051d25 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 30 Jul 2024 18:40:08 +0200 Subject: [PATCH 34/56] fix: remove unnecessary api functions --- pallets/api/src/fungibles/mod.rs | 39 --------- pallets/api/src/fungibles/tests.rs | 5 +- .../contracts/fungibles/lib.rs | 15 ---- .../integration-tests/src/local_fungibles.rs | 87 ------------------- pop-api/src/v0/assets/fungibles.rs | 48 ---------- runtime/devnet/src/config/api.rs | 3 - 6 files changed, 1 insertion(+), 196 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 9579a66c..00b71401 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -21,7 +21,6 @@ type AssetIdParameterOf = >>:: type AssetsOf = pallet_assets::Pallet>; type AssetsInstanceOf = ::AssetsInstance; type AssetsWeightInfoOf = >>::WeightInfo; -type RemoveItemsLimitOf = >>::RemoveItemsLimit; type BalanceOf = > as Inspect< ::AccountId, >>::Balance; @@ -209,44 +208,6 @@ pub mod pallet { AssetsOf::::start_destroy(origin, id.into()) } - /// Destroy all accounts associated with a token with a given asset ID. - /// - /// # Parameters - /// - `id` - The ID of the asset. - // TODO: weight function - #[pallet::call_index(13)] - #[pallet::weight(AssetsWeightInfoOf::::destroy_accounts(RemoveItemsLimitOf::::get() / 6))] - pub fn destroy_accounts( - origin: OriginFor, - id: AssetIdOf, - ) -> DispatchResultWithPostInfo { - AssetsOf::::destroy_accounts(origin, id.into()) - } - - /// Destroy all approvals associated with a token with a given asset ID. - /// - /// # Parameters - /// - `id` - The ID of the asset. - // TODO: weight function - #[pallet::call_index(14)] - #[pallet::weight(AssetsWeightInfoOf::::destroy_approvals(RemoveItemsLimitOf::::get() / 4))] - pub fn destroy_approvals( - origin: OriginFor, - id: AssetIdOf, - ) -> DispatchResultWithPostInfo { - AssetsOf::::destroy_approvals(origin, id.into()) - } - - /// Complete the process of destroying a token with a given asset ID. - /// - /// # Parameters - /// - `id` - The ID of the asset. - #[pallet::call_index(15)] - #[pallet::weight(AssetsWeightInfoOf::::finish_destroy())] - pub fn finish_destroy(origin: OriginFor, id: AssetIdOf) -> DispatchResult { - AssetsOf::::finish_destroy(origin, id.into()) - } - /// Set the metadata for a token with a given asset ID. /// /// # Parameters diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 735aadf1..5ef08f63 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -69,13 +69,10 @@ fn create_works() { } #[test] -fn destroy_asset_works() { +fn start_destroy_works() { new_test_ext().execute_with(|| { create_asset(ALICE, ASSET, 100); assert_ok!(Fungibles::start_destroy(signed(ALICE), ASSET)); - assert_ok!(Fungibles::destroy_accounts(signed(ALICE), ASSET)); - assert_ok!(Fungibles::destroy_approvals(signed(ALICE), ASSET)); - assert_ok!(Fungibles::finish_destroy(signed(ALICE), ASSET)); }); } diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs index 105d5564..b3728560 100755 --- a/pop-api/integration-tests/contracts/fungibles/lib.rs +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -141,21 +141,6 @@ mod fungibles { api::start_destroy(id) } - #[ink(message)] - pub fn destroy_accounts(&self, id: AssetId) -> Result<()> { - api::destroy_accounts(id) - } - - #[ink(message)] - pub fn destroy_approvals(&self, id: AssetId) -> Result<()> { - api::destroy_approvals(id) - } - - #[ink(message)] - pub fn finish_destroy(&self, id: AssetId) -> Result<()> { - api::finish_destroy(id) - } - #[ink(message)] pub fn set_metadata( &self, diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 33d43c40..096c16cf 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -128,27 +128,6 @@ fn start_destroy(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { result } -fn destroy_accounts(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { - let function = function_selector("destroy_accounts"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - result -} - -fn destroy_approvals(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { - let function = function_selector("destroy_approvals"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - result -} - -fn finish_destroy(addr: AccountId32, asset_id: AssetId) -> ExecReturnValue { - let function = function_selector("finish_destroy"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - result -} - fn set_metadata( addr: AccountId32, asset_id: AssetId, @@ -605,72 +584,6 @@ fn start_destroy_works() { }); } -#[test] -fn destroy_accounts_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!( - decoded::(destroy_accounts(addr.clone(), ASSET_ID)), - Ok(Module { index: 52, error: 3 }), - ); - let asset = create_asset(addr.clone(), ASSET_ID, 1); - // The destroy process hasn't been started. - assert_eq!( - decoded::(destroy_accounts(addr.clone(), asset)), - Ok(Module { index: 52, error: 17 }), - ); - start_destroy_asset(addr.clone(), asset); - let result = destroy_accounts(addr.clone(), asset); - assert!(!result.did_revert(), "Contract reverted!"); - }); -} - -#[test] -fn destroy_approvals_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!( - decoded::(destroy_approvals(addr.clone(), ASSET_ID)), - Ok(Module { index: 52, error: 3 }), - ); - let asset = create_asset(addr.clone(), ASSET_ID, 1); - // The destroy process hasn't been started. - assert_eq!( - decoded::(destroy_approvals(addr.clone(), asset)), - Ok(Module { index: 52, error: 17 }), - ); - start_destroy_asset(addr.clone(), asset); - let result = destroy_approvals(addr.clone(), asset); - assert!(!result.did_revert(), "Contract reverted!"); - }); -} - -#[test] -fn finish_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!( - decoded::(finish_destroy(addr.clone(), ASSET_ID)), - Ok(Module { index: 52, error: 3 }), - ); - let asset = create_asset(addr.clone(), ASSET_ID, 1); - // The destroy process hasn't been started. - assert_eq!( - decoded::(finish_destroy(addr.clone(), asset)), - Ok(Module { index: 52, error: 17 }), - ); - start_destroy_asset(addr.clone(), asset); - let result = finish_destroy(addr.clone(), asset); - assert!(!result.did_revert(), "Contract reverted!"); - }); -} - #[test] fn set_metadata_works() { new_test_ext().execute_with(|| { diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index ec2b9059..6f9774d3 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -48,9 +48,6 @@ mod constants { /// 3. Asset Management: pub(super) const CREATE: u8 = 11; pub(super) const START_DESTROY: u8 = 12; - pub(super) const DESTROY_ACCOUNTS: u8 = 13; - pub(super) const DESTROY_APPROVALS: u8 = 14; - pub(super) const FINISH_DESTROY: u8 = 15; pub(super) const SET_METADATA: u8 = 16; pub(super) const CLEAR_METADATA: u8 = 17; } @@ -287,51 +284,6 @@ pub mod asset_management { .call(&(id)) } - /// Destroy all accounts associated with a token with a given asset ID. - /// - /// # Parameters - /// - `id` - The ID of the asset. - /// - /// # Returns - /// Returns `Ok(())` if successful, or an error if the operation fails. - pub fn destroy_accounts(id: AssetId) -> Result<()> { - build_dispatch(DESTROY_ACCOUNTS) - .input::() - .output::, true>() - .handle_error_code::() - .call(&(id)) - } - - /// Destroy all approvals associated with a token with a given asset ID. - /// - /// # Parameters - /// - `id` - The ID of the asset. - /// - /// # Returns - /// Returns `Ok(())` if successful, or an error if the operation fails. - pub fn destroy_approvals(id: AssetId) -> Result<()> { - build_dispatch(DESTROY_APPROVALS) - .input::() - .output::, true>() - .handle_error_code::() - .call(&(id)) - } - - /// Complete the process of destroying a token with a given asset ID. - /// - /// # Parameters - /// - `id` - The ID of the asset. - /// - /// # Returns - /// Returns `Ok(())` if successful, or an error if the operation fails. - pub fn finish_destroy(id: AssetId) -> Result<()> { - build_dispatch(FINISH_DESTROY) - .input::() - .output::, true>() - .handle_error_code::() - .call(&(id)) - } - /// Set the metadata for a token with a given asset ID. /// /// # Parameters diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index bc38a3c3..1f6bdfe8 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -25,9 +25,6 @@ impl Contains for AllowedApiCalls { | approve { .. } | increase_allowance { .. } | create { .. } | set_metadata { .. } | start_destroy { .. } - | destroy_accounts { .. } - | destroy_approvals { .. } - | finish_destroy { .. } | clear_metadata { .. } ) ) From 51c38ccca26ca4fb219f1462d38716e0363303a1 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 10:22:26 +0200 Subject: [PATCH 35/56] refactor: match statement --- .../integration-tests/src/local_fungibles.rs | 66 +++++++------------ 1 file changed, 22 insertions(+), 44 deletions(-) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 6d1a2ebc..54f63263 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -441,10 +441,8 @@ fn total_supply(addr: AccountId32, asset_id: AssetId) -> Result let function = function_selector("total_supply"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call balance_of contract message. @@ -452,10 +450,8 @@ fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Resul 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"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call allowance contract message. @@ -468,10 +464,8 @@ 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"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call token_name contract message. @@ -479,10 +473,8 @@ fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { let function = function_selector("token_name"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::, Error>>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::, Error>>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call token_symbol contract message. @@ -490,10 +482,8 @@ fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> let function = function_selector("token_symbol"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::, Error>>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::, Error>>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call token_decimals contract message. @@ -501,10 +491,8 @@ fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { let function = function_selector("token_decimals"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn transfer( @@ -516,10 +504,8 @@ fn transfer( let function = function_selector("transfer"); let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn transfer_from( @@ -535,10 +521,8 @@ fn transfer_from( [function, asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()] .concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn approve( @@ -550,10 +534,8 @@ fn approve( let function = function_selector("approve"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn increase_allowance( @@ -565,10 +547,8 @@ fn increase_allowance( let function = function_selector("increase_allowance"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn decrease_allowance( @@ -580,10 +560,8 @@ fn decrease_allowance( let function = function_selector("decrease_allowance"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // fn asset_exists(addr: AccountId32, asset_id: AssetId) -> bool { From 6084f89512b20bf5358a1e18a7184993f1f3f046 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 12:45:43 +0200 Subject: [PATCH 36/56] feat: burn and mint --- pallets/api/src/fungibles/mod.rs | 36 +++ pallets/api/src/fungibles/tests.rs | 34 ++- .../contracts/fungibles/lib.rs | 30 ++- .../integration-tests/src/local_fungibles.rs | 221 +++++++++--------- pop-api/src/v0/assets/fungibles.rs | 43 +++- runtime/devnet/src/config/api.rs | 1 + 6 files changed, 243 insertions(+), 122 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 1e0fcbe9..82b9ad0f 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -290,6 +290,42 @@ pub mod pallet { pub fn clear_metadata(origin: OriginFor, id: AssetIdOf) -> DispatchResult { AssetsOf::::clear_metadata(origin, id.into()) } + + /// Creates `amount` tokens and assigns them to `account`, increasing the total supply. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// - `owner` - The account to be credited with the created tokens. + /// - `value` - The number of tokens to mint. + #[pallet::call_index(18)] + #[pallet::weight(AssetsWeightInfoOf::::mint())] + pub fn mint( + origin: OriginFor, + id: AssetIdOf, + account: AccountIdOf, + amount: BalanceOf, + ) -> DispatchResult { + let account = T::Lookup::unlookup(account); + AssetsOf::::mint(origin, id.into(), account, amount) + } + + /// Destroys `amount` tokens from `account`, reducing the total supply. + /// + /// # Parameters + /// - `id` - The ID of the asset. + /// - `owner` - The account from which the tokens will be destroyed. + /// - `value` - The number of tokens to destroy. + #[pallet::call_index(19)] + #[pallet::weight(AssetsWeightInfoOf::::burn())] + pub fn burn( + origin: OriginFor, + id: AssetIdOf, + account: AccountIdOf, + amount: BalanceOf, + ) -> DispatchResult { + let account = T::Lookup::unlookup(account); + AssetsOf::::burn(origin, id.into(), account, amount) + } } impl Pallet { diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 558c8073..623bd158 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -108,7 +108,7 @@ fn create_works() { #[test] fn start_destroy_works() { new_test_ext().execute_with(|| { - create_asset(ALICE, ASSET, 100); + create_asset(ALICE, ASSET); assert_ok!(Fungibles::start_destroy(signed(ALICE), ASSET)); }); } @@ -119,7 +119,7 @@ fn set_metadata_works() { let name = vec![42]; let symbol = vec![42]; let decimals = 42; - create_asset(ALICE, ASSET, 100); + create_asset(ALICE, ASSET); assert_ok!(Fungibles::set_metadata( signed(ALICE), ASSET, @@ -147,6 +147,30 @@ fn clear_metadata_works() { }); } +#[test] +fn mint_works() { + new_test_ext().execute_with(|| { + let amount: Balance = 100 * UNIT; + create_asset(ALICE, ASSET); + let balance_before_mint = Assets::balance(ASSET, &BOB); + assert_ok!(Fungibles::mint(signed(ALICE), ASSET, BOB, amount)); + let balance_after_mint = Assets::balance(ASSET, &BOB); + assert_eq!(balance_after_mint, balance_before_mint + amount); + }); +} + +#[test] +fn burn_works() { + new_test_ext().execute_with(|| { + let amount: Balance = 100 * UNIT; + create_asset_and_mint_to(ALICE, ASSET, BOB, amount); + let balance_before_burn = Assets::balance(ASSET, &BOB); + assert_ok!(Fungibles::burn(signed(ALICE), ASSET, BOB, amount)); + let balance_after_burn = Assets::balance(ASSET, &BOB); + assert_eq!(balance_after_burn, balance_before_burn - amount); + }); +} + #[test] fn total_supply_works() { new_test_ext().execute_with(|| { @@ -194,8 +218,8 @@ fn signed(account: AccountId) -> RuntimeOrigin { RuntimeOrigin::signed(account) } -fn create_asset(owner: AccountId, asset_id: AssetId, min_balance: Balance) { - assert_ok!(Assets::create(signed(owner), asset_id, owner, min_balance)); +fn create_asset(owner: AccountId, asset_id: AssetId) { + assert_ok!(Assets::create(signed(owner), asset_id, owner, 1)); } fn mint_asset(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance) { @@ -203,7 +227,7 @@ fn mint_asset(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance } fn create_asset_and_mint_to(owner: AccountId, asset_id: AssetId, to: AccountId, value: Balance) { - create_asset(owner, asset_id, 1); + create_asset(owner, asset_id); mint_asset(owner, asset_id, to, value) } diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs index b3728560..751dbfd2 100755 --- a/pop-api/integration-tests/contracts/fungibles/lib.rs +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -122,14 +122,14 @@ mod fungibles { api::token_decimals(id) } - // 3. Asset Management: - // - create - // - start_destroy - // - destroy_accounts - // - destroy_approvals - // - finish_destroy - // - set_metadata - // - clear_metadata + /// 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<()> { @@ -157,6 +157,20 @@ mod fungibles { api::clear_metadata(id) } + /// 4. PSP-22 Mintable & Burnable Interface: + /// - mint + /// - burn + + #[ink(message)] + pub fn mint(&self, id: AssetId, account: AccountId, amount: Balance) -> Result<()> { + api::mint(id, account, amount) + } + + #[ink(message)] + pub fn burn(&self, id: AssetId, account: AccountId, amount: Balance) -> Result<()> { + api::burn(id, account, amount) + } + // #[ink(message)] // pub fn asset_exists(&self, id: AssetId) -> Result { // // api::asset_exists(id).map_err(|e| e.into()) diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/local_fungibles.rs index 2f568ece..7fa4ec52 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/local_fungibles.rs @@ -446,6 +446,69 @@ fn clear_metadata_works() { }); } +#[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); + // Minting can only be done by the owner. + assert_eq!(mint(addr.clone(), asset, BOB, 1), Err(Module { index: 52, error: 2 })); + 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 })); + thaw_asset(addr.clone(), asset); + // 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); + 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 })); + }); +} + +#[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 })); + 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 })); + // 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 })); + 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 })); + thaw_asset(addr.clone(), asset); + // 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); + 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: 16 })); + }); +} + // #[test] // #[ignore] // fn asset_exists_works() { @@ -463,66 +526,6 @@ fn clear_metadata_works() { // }); // } -// #[test] -// #[ignore] -// 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!( -// decoded::(transfer_from(addr.clone(), 1, None, Some(BOB), amount, &[0u8])), -// Token(UnknownAsset) -// ); -// let asset = create_asset(ALICE, 1, 2); -// // Minting can only be done by the owner. -// assert_eq!( -// decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8])), -// Ok(Module { index: 52, error: 2 }), -// ); -// // Minimum balance of an asset can not be zero. -// assert_eq!( -// decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), 1, &[0u8])), -// Token(BelowMinimum) -// ); -// let asset = create_asset(addr.clone(), 2, 2); -// // Asset is not live, i.e. frozen or being destroyed. -// freeze_asset(addr.clone(), asset); -// assert_eq!( -// decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8])), -// Ok(Module { index: 52, error: 16 }), -// ); -// thaw_asset(addr.clone(), asset); -// // Successful mint. -// let balance_before_mint = Assets::balance(asset, &BOB); -// let result = transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8]); -// assert!(!result.did_revert(), "Contract reverted!"); -// let balance_after_mint = Assets::balance(asset, &BOB); -// assert_eq!(balance_after_mint, balance_before_mint + amount); -// // Can not mint more tokens than Balance::MAX. -// assert_eq!( -// decoded::(transfer_from( -// addr.clone(), -// asset, -// None, -// Some(BOB), -// Balance::MAX, -// &[0u8] -// )), -// Arithmetic(Overflow) -// ); -// // Asset is not live, i.e. frozen or being destroyed. -// start_destroy_asset(addr.clone(), asset); -// assert_eq!( -// decoded::(transfer_from(addr.clone(), asset, None, Some(BOB), amount, &[0u8])), -// Ok(Module { index: 52, error: 16 }), -// ); -// }); -// } - fn decoded(result: ExecReturnValue) -> Result { ::decode(&mut &result.data[1..]).map_err(|_| result) } @@ -532,10 +535,8 @@ fn total_supply(addr: AccountId32, asset_id: AssetId) -> Result let function = function_selector("total_supply"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call balance_of contract message. @@ -543,10 +544,8 @@ fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Resul 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"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call allowance contract message. @@ -559,10 +558,8 @@ 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"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call token_name contract message. @@ -570,10 +567,8 @@ fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { let function = function_selector("token_name"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::, Error>>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::, Error>>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call token_symbol contract message. @@ -581,10 +576,8 @@ fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> let function = function_selector("token_symbol"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::, Error>>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::, Error>>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } // Call token_decimals contract message. @@ -592,10 +585,8 @@ fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { let function = function_selector("token_decimals"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn transfer( @@ -607,10 +598,8 @@ fn transfer( let function = function_selector("transfer"); let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn transfer_from( @@ -626,10 +615,8 @@ fn transfer_from( [function, asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()] .concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn approve( @@ -641,10 +628,8 @@ fn approve( let function = function_selector("approve"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn increase_allowance( @@ -656,10 +641,8 @@ fn increase_allowance( let function = function_selector("increase_allowance"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn decrease_allowance( @@ -671,10 +654,8 @@ fn decrease_allowance( let function = function_selector("decrease_allowance"); let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn create( @@ -694,10 +675,8 @@ fn start_destroy(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { let function = function_selector("start_destroy"); let params = [function, asset_id.encode()].concat(); let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } fn set_metadata( @@ -723,6 +702,32 @@ fn clear_metadata(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } +fn mint( + addr: AccountId32, + asset_id: AssetId, + account: AccountId32, + amount: Balance, +) -> Result<(), Error> { + let function = function_selector("mint"); + let params = [function, asset_id.encode(), account.encode(), amount.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +fn burn( + addr: AccountId32, + asset_id: AssetId, + account: AccountId32, + amount: Balance, +) -> Result<(), Error> { + let function = function_selector("burn"); + let params = [function, asset_id.encode(), account.encode(), amount.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + fn create_asset(owner: AccountId32, asset_id: AssetId, min_balance: Balance) -> AssetId { assert_ok!(Assets::create( RuntimeOrigin::signed(owner.clone()), diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 844b08b1..005fd16b 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -28,6 +28,7 @@ fn build_read_state(state_query: u8) -> ChainExtensionMethod<(), (), (), false> /// 1. PSP-22 Interface /// 2. PSP-22 Metadata Interface /// 3. Asset Management +/// 4. PSP-22 Mintable & Burnable Interface mod constants { /// 1. PSP-22 Interface: @@ -50,6 +51,10 @@ mod constants { pub(super) const START_DESTROY: u8 = 12; pub(super) const SET_METADATA: u8 = 16; pub(super) const CLEAR_METADATA: u8 = 17; + + /// 4. PSP-22 Mintable & Burnable interface: + pub(super) const MINT: u8 = 18; + pub(super) const BURN: u8 = 19; } /// Returns the total token supply for a given asset ID. @@ -199,8 +204,43 @@ pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re .call(&(id, spender, value)) } +/// Creates `amount` tokens and assigns them to `account`, increasing the total supply. +/// +/// # Parameters +/// - `id` - The ID of the asset. +/// - `owner` - The account to be credited with the created tokens. +/// - `value` - The number of tokens to mint. +/// +/// # Returns +/// Returns `Ok(())` if successful, or an error if the operation fails. +pub fn mint(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { + build_dispatch(MINT) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, account, amount)) +} + +/// Destroys `amount` tokens from `account`, reducing the total supply. +/// +/// # Parameters +/// - `id` - The ID of the asset. +/// - `owner` - The account from which the tokens will be destroyed. +/// - `value` - The number of tokens to destroy. +/// +/// # Returns +/// Returns `Ok(())` if successful, or an error if the operation fails. +pub fn burn(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { + build_dispatch(BURN) + .input::<(AssetId, AccountId, Balance)>() + .output::, true>() + .handle_error_code::() + .call(&(id, account, amount)) +} + pub mod metadata { use super::*; + /// Returns the token name for a given asset ID. /// /// # Parameters @@ -252,6 +292,7 @@ pub mod metadata { pub mod asset_management { use super::*; + /// Create a new token with a given asset ID. /// /// # Parameters @@ -316,7 +357,7 @@ pub mod asset_management { .handle_error_code::() .call(&(id)) } - // + // pub fn asset_exists(id: AssetId) -> Result { // assets::asset_exists(id) // } diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 14574ffc..d8e01bfb 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -28,6 +28,7 @@ impl Contains for AllowedApiCalls { | create { .. } | set_metadata { .. } | start_destroy { .. } | clear_metadata { .. } + | mint { .. } | burn { .. } ) ) } From 584b9c6db5e3db4dbf42358bea97a28d29acf4ee Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 16:55:21 +0200 Subject: [PATCH 37/56] feat: create asset in constructor --- pallets/api/src/fungibles/mod.rs | 4 + pallets/api/src/fungibles/tests.rs | 8 + .../create_token_in_constructor/Cargo.toml | 21 ++ .../create_token_in_constructor/lib.rs | 45 +++ .../contracts/fungibles/lib.rs | 9 +- .../{local_fungibles.rs => fungibles/mod.rs} | 340 ++---------------- .../integration-tests/src/fungibles/utils.rs | 333 +++++++++++++++++ pop-api/integration-tests/src/lib.rs | 2 +- pop-api/src/v0/assets/fungibles.rs | 18 +- runtime/devnet/src/config/api.rs | 2 +- 10 files changed, 460 insertions(+), 322 deletions(-) create mode 100755 pop-api/integration-tests/contracts/create_token_in_constructor/Cargo.toml create mode 100755 pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs rename pop-api/integration-tests/src/{local_fungibles.rs => fungibles/mod.rs} (64%) create mode 100644 pop-api/integration-tests/src/fungibles/utils.rs diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 1e0fcbe9..73835b17 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -75,6 +75,9 @@ pub mod pallet { /// Token decimals for a given asset ID. #[codec(index = 10)] TokenDecimals(AssetIdOf), + /// Check if token exists for a given asset ID. + #[codec(index = 18)] + AssetExists(AssetIdOf), } /// Configure the pallet by specifying the parameters and types on which it depends. @@ -319,6 +322,7 @@ pub mod pallet { TokenDecimals(id) => { as MetadataInspect>>::decimals(id).encode() }, + AssetExists(id) => AssetsOf::::asset_exists(id).encode(), } } diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 558c8073..b464a13a 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -190,6 +190,14 @@ fn token_metadata_works() { }); } +#[test] +fn asset_exists_works() { + new_test_ext().execute_with(|| { + create_asset(ALICE, ASSET, 100); + assert_eq!(Assets::asset_exists(ASSET).encode(), Fungibles::read_state(AssetExists(ASSET))); + }); +} + fn signed(account: AccountId) -> RuntimeOrigin { RuntimeOrigin::signed(account) } 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 new file mode 100755 index 00000000..2c202715 --- /dev/null +++ b/pop-api/integration-tests/contracts/create_token_in_constructor/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "create_token_in_constructor" +version = "0.1.0" +authors = ["[your_name] <[your_email]>"] +edition = "2021" + +[dependencies] +ink = { version = "5.0.0", default-features = false } +pop-api = { path = "../../..", default-features = false, features = ["fungibles"] } + +[lib] +path = "lib.rs" + +[features] +default = ["std"] +std = [ + "ink/std", + "pop-api/std", +] +ink-as-dependency = [] +e2e-tests = [] diff --git a/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs b/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs new file mode 100755 index 00000000..3a800382 --- /dev/null +++ b/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs @@ -0,0 +1,45 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +use ink::prelude::vec::Vec; +use pop_api::{ + assets::fungibles::{self as api}, + primitives::AssetId, + StatusCode, +}; + +pub type Result = core::result::Result; + +#[ink::contract] +mod create_token_in_constructor { + use super::*; + + #[ink(storage)] + pub struct Fungible { + id: AssetId, + } + + impl Fungible { + #[ink(constructor, payable)] + pub fn new(id: AssetId, min_balance: Balance) -> Result { + let contract = Self { id }; + let account = contract.env().account_id(); + api::create(id, account, min_balance)?; + Ok(contract) + } + + #[ink(message)] + pub fn asset_exists(&self) -> Result { + api::asset_exists(self.id) + } + } + + #[cfg(test)] + mod tests { + use super::*; + + #[ink::test] + fn default_works() { + PopApiFungiblesExample::new(); + } + } +} diff --git a/pop-api/integration-tests/contracts/fungibles/lib.rs b/pop-api/integration-tests/contracts/fungibles/lib.rs index b3728560..888dd86d 100755 --- a/pop-api/integration-tests/contracts/fungibles/lib.rs +++ b/pop-api/integration-tests/contracts/fungibles/lib.rs @@ -157,11 +157,10 @@ mod fungibles { api::clear_metadata(id) } - // #[ink(message)] - // pub fn asset_exists(&self, id: AssetId) -> Result { - // // api::asset_exists(id).map_err(|e| e.into()) - // api::asset_exists(id) - // } + #[ink(message)] + pub fn asset_exists(&self, id: AssetId) -> Result { + api::asset_exists(id) + } } #[cfg(test)] diff --git a/pop-api/integration-tests/src/local_fungibles.rs b/pop-api/integration-tests/src/fungibles/mod.rs similarity index 64% rename from pop-api/integration-tests/src/local_fungibles.rs rename to pop-api/integration-tests/src/fungibles/mod.rs index 1a1ea3bf..ca3922b3 100644 --- a/pop-api/integration-tests/src/local_fungibles.rs +++ b/pop-api/integration-tests/src/fungibles/mod.rs @@ -4,6 +4,9 @@ use pop_primitives::error::{ Error::{self, *}, TokenError::*, }; +use utils::*; + +mod utils; const ASSET_ID: AssetId = 1; const CONTRACT: &str = "contracts/fungibles/target/ink/fungibles.wasm"; @@ -315,6 +318,7 @@ fn token_metadata_works() { /// - destroy_approvals /// - finish_destroy /// - set_metadata +/// - asset_exists #[test] fn create_works() { @@ -348,6 +352,17 @@ fn create_works() { }); } +#[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"; + let addr = instantiate_and_create_fungible(contract, ASSET_ID, 1); + assert_eq!(asset_exists(addr, ASSET_ID), Ok(Assets::asset_exists(ASSET_ID))); + }); +} + #[test] fn start_destroy_works() { new_test_ext().execute_with(|| { @@ -446,22 +461,20 @@ fn clear_metadata_works() { }); } -// #[test] -// #[ignore] -// fn asset_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!(Assets::asset_exists(ASSET_ID), asset_exists(addr.clone(), ASSET_ID)); -// -// // Tokens in circulation. -// create_asset(addr.clone(), ASSET_ID, 1); -// assert_eq!(Assets::asset_exists(ASSET_ID), asset_exists(addr, ASSET_ID)); -// }); -// } +#[test] +fn asset_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!(asset_exists(addr.clone(), ASSET_ID), Ok(Assets::asset_exists(ASSET_ID))); + + // Tokens in circulation. + create_asset(addr.clone(), ASSET_ID, 1); + assert_eq!(asset_exists(addr.clone(), ASSET_ID), Ok(Assets::asset_exists(ASSET_ID))); + }); +} // #[test] // #[ignore] @@ -522,298 +535,3 @@ fn clear_metadata_works() { // ); // }); // } - -fn decoded(result: ExecReturnValue) -> Result { - ::decode(&mut &result.data[1..]).map_err(|_| result) -} - -// Call total_supply contract message. -fn total_supply(addr: AccountId32, asset_id: AssetId) -> Result { - 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.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -// Call balance_of contract message. -fn balance_of(addr: AccountId32, asset_id: AssetId, owner: AccountId32) -> Result { - 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.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -// Call allowance contract message. -fn allowance( - addr: AccountId32, - asset_id: AssetId, - owner: AccountId32, - spender: AccountId32, -) -> Result { - 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.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -// Call token_name contract message. -fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { - let function = function_selector("token_name"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::, Error>>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -// Call token_symbol contract message. -fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> { - let function = function_selector("token_symbol"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::, Error>>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -// Call token_decimals contract message. -fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { - 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.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn transfer( - addr: AccountId32, - asset_id: AssetId, - to: AccountId32, - value: Balance, -) -> Result<(), Error> { - let function = function_selector("transfer"); - let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn transfer_from( - addr: AccountId32, - asset_id: AssetId, - from: AccountId32, - to: AccountId32, - value: Balance, -) -> Result<(), Error> { - let function = function_selector("transfer_from"); - let data: Vec = vec![]; - let params = - [function, asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()] - .concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn approve( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, - value: Balance, -) -> Result<(), Error> { - let function = function_selector("approve"); - let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn increase_allowance( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, - value: Balance, -) -> Result<(), Error> { - let function = function_selector("increase_allowance"); - let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn decrease_allowance( - addr: AccountId32, - asset_id: AssetId, - spender: AccountId32, - value: Balance, -) -> Result<(), Error> { - let function = function_selector("decrease_allowance"); - let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn create( - addr: AccountId32, - asset_id: AssetId, - admin: AccountId32, - min_balance: Balance, -) -> Result<(), Error> { - let function = function_selector("create"); - let params = [function, asset_id.encode(), admin.encode(), min_balance.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn start_destroy(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { - let function = function_selector("start_destroy"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - match decoded::>(result) { - Ok(x) => x, - Err(result) => panic!("Contract reverted: {:?}", result), - } -} - -fn set_metadata( - addr: AccountId32, - asset_id: AssetId, - name: Vec, - symbol: Vec, - decimals: u8, -) -> Result<(), Error> { - let function = function_selector("set_metadata"); - let params = - [function, asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -fn clear_metadata(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { - let function = function_selector("clear_metadata"); - let params = [function, asset_id.encode()].concat(); - let result = bare_call(addr, params, 0).expect("should work"); - decoded::>(result.clone()) - .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) -} - -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 -} - -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 -} - -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. -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 -} - -// Freeze an asset. -fn freeze_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::freeze_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} - -// Thaw an asset. -fn thaw_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::thaw_asset(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} - -// Start destroying an asset. -fn start_destroy_asset(owner: AccountId32, asset_id: AssetId) { - assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(owner.into()), asset_id.into())); -} - -// Create an asset and set metadata. -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 -} - -// Set metadata of an asset. -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 - )); -} - -fn token_name_asset(asset_id: AssetId) -> Vec { - as MetadataInspect>::name( - asset_id, - ) -} - -fn token_symbol_asset(asset_id: AssetId) -> Vec { - as MetadataInspect>::symbol( - asset_id, - ) -} - -fn token_decimals_asset(asset_id: AssetId) -> u8 { - as MetadataInspect>::decimals( - asset_id, - ) -} diff --git a/pop-api/integration-tests/src/fungibles/utils.rs b/pop-api/integration-tests/src/fungibles/utils.rs new file mode 100644 index 00000000..555bfd50 --- /dev/null +++ b/pop-api/integration-tests/src/fungibles/utils.rs @@ -0,0 +1,333 @@ +use super::*; + +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 function = function_selector("total_supply"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn balance_of( + addr: AccountId32, + asset_id: AssetId, + owner: AccountId32, +) -> Result { + 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.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn allowance( + addr: AccountId32, + asset_id: AssetId, + owner: AccountId32, + spender: AccountId32, +) -> Result { + 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.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn token_name(addr: AccountId32, asset_id: AssetId) -> Result, Error> { + let function = function_selector("token_name"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::, Error>>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn token_symbol(addr: AccountId32, asset_id: AssetId) -> Result, Error> { + let function = function_selector("token_symbol"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::, Error>>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn token_decimals(addr: AccountId32, asset_id: AssetId) -> Result { + 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.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn asset_exists(addr: AccountId32, asset_id: AssetId) -> Result { + let function = function_selector("asset_exists"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn transfer( + addr: AccountId32, + asset_id: AssetId, + to: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("transfer"); + let params = [function, asset_id.encode(), to.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn transfer_from( + addr: AccountId32, + asset_id: AssetId, + from: AccountId32, + to: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("transfer_from"); + let data: Vec = vec![]; + let params = + [function, asset_id.encode(), from.encode(), to.encode(), value.encode(), data.encode()] + .concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn approve( + addr: AccountId32, + asset_id: AssetId, + spender: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("approve"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn increase_allowance( + addr: AccountId32, + asset_id: AssetId, + spender: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("increase_allowance"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn decrease_allowance( + addr: AccountId32, + asset_id: AssetId, + spender: AccountId32, + value: Balance, +) -> Result<(), Error> { + let function = function_selector("decrease_allowance"); + let params = [function, asset_id.encode(), spender.encode(), value.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn create( + addr: AccountId32, + asset_id: AssetId, + admin: AccountId32, + min_balance: Balance, +) -> Result<(), Error> { + let function = function_selector("create"); + let params = [function, asset_id.encode(), admin.encode(), min_balance.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn start_destroy(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { + let function = function_selector("start_destroy"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + match decoded::>(result) { + Ok(x) => x, + Err(result) => panic!("Contract reverted: {:?}", result), + } +} + +pub(super) fn set_metadata( + addr: AccountId32, + asset_id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, +) -> Result<(), Error> { + let function = function_selector("set_metadata"); + let params = + [function, asset_id.encode(), name.encode(), symbol.encode(), decimals.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) +} + +pub(super) fn clear_metadata(addr: AccountId32, asset_id: AssetId) -> Result<(), Error> { + let function = function_selector("clear_metadata"); + let params = [function, asset_id.encode()].concat(); + let result = bare_call(addr, params, 0).expect("should work"); + 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 +} + +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 +} + +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 +} + +// 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())); +} + +// 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())); +} + +// 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 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 +} + +// 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 + )); +} + +pub(super) fn token_name_asset(asset_id: AssetId) -> Vec { + as MetadataInspect>::name( + asset_id, + ) +} + +pub(super) fn token_symbol_asset(asset_id: AssetId) -> Vec { + as MetadataInspect>::symbol( + asset_id, + ) +} + +pub(super) fn token_decimals_asset(asset_id: AssetId) -> u8 { + as MetadataInspect>::decimals( + asset_id, + ) +} + +pub(super) fn instantiate_and_create_fungible( + contract: &str, + asset_id: AssetId, + min_balance: Balance, +) -> AccountId32 { + 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 result = Contracts::bare_instantiate( + ALICE, + INIT_VALUE, + GAS_LIMIT, + None, + Code::Upload(wasm_binary), + input, + vec![], + DEBUG_OUTPUT, + CollectEvents::Skip, + ) + .result + .unwrap(); + assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); + result.account_id +} diff --git a/pop-api/integration-tests/src/lib.rs b/pop-api/integration-tests/src/lib.rs index ea5ccb05..b728fca6 100644 --- a/pop-api/integration-tests/src/lib.rs +++ b/pop-api/integration-tests/src/lib.rs @@ -16,7 +16,7 @@ use pop_runtime_devnet::{ UNIT, }; -mod local_fungibles; +mod fungibles; type AssetId = u32; type Balance = u128; diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 844b08b1..be8d44af 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -50,6 +50,7 @@ mod constants { pub(super) const START_DESTROY: u8 = 12; pub(super) const SET_METADATA: u8 = 16; pub(super) const CLEAR_METADATA: u8 = 17; + pub(super) const ASSET_EXISTS: u8 = 18; } /// Returns the total token supply for a given asset ID. @@ -316,10 +317,19 @@ pub mod asset_management { .handle_error_code::() .call(&(id)) } - // - // pub fn asset_exists(id: AssetId) -> Result { - // assets::asset_exists(id) - // } + + /// Checks if token exists for a given asset ID. + /// + /// # Parameters + /// - `id` - The ID of the asset. + #[inline] + pub fn asset_exists(id: AssetId) -> Result { + build_read_state(ASSET_EXISTS) + .input::() + .output::, true>() + .handle_error_code::() + .call(&(id)) + } } /// Represents various errors related to local fungible assets in the Pop API. diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index 14574ffc..66c6c58b 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -43,7 +43,7 @@ impl Contains> for AllowedApiCalls { TotalSupply(..) | BalanceOf { .. } | Allowance { .. } | TokenName(..) | TokenSymbol(..) - | TokenDecimals(..) + | TokenDecimals(..) | AssetExists(..) ) ) } From 772514c10bde521545e6adfa18870deb2550ec7b Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 17:33:08 +0200 Subject: [PATCH 38/56] refactor: IT create token in constructor --- pallets/api/src/fungibles/mod.rs | 2 +- .../contracts/create_token_in_constructor/lib.rs | 5 +++-- pop-api/integration-tests/src/fungibles/mod.rs | 11 +++++++++-- pop-api/integration-tests/src/fungibles/utils.rs | 9 +++++---- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 73835b17..8c2ae26b 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -303,7 +303,7 @@ pub mod pallet { /// /// # Parameter /// - `value` - An instance of `Read`, which specifies the type of state query and - /// the associated parameters. + /// the associated parameters. pub fn read_state(value: Read) -> Vec { use Read::*; diff --git a/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs b/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs index 3a800382..e9e5d127 100755 --- a/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs +++ b/pop-api/integration-tests/contracts/create_token_in_constructor/lib.rs @@ -22,8 +22,9 @@ mod create_token_in_constructor { #[ink(constructor, payable)] pub fn new(id: AssetId, min_balance: Balance) -> Result { let contract = Self { id }; - let account = contract.env().account_id(); - api::create(id, account, min_balance)?; + // AccountId of the contract which will be set to the owner of the fungible token. + let owner = contract.env().account_id(); + api::create(id, owner, min_balance)?; Ok(contract) } diff --git a/pop-api/integration-tests/src/fungibles/mod.rs b/pop-api/integration-tests/src/fungibles/mod.rs index ca3922b3..81a276a9 100644 --- a/pop-api/integration-tests/src/fungibles/mod.rs +++ b/pop-api/integration-tests/src/fungibles/mod.rs @@ -358,8 +358,15 @@ fn instantiate_and_create_fungible_works() { let _ = env_logger::try_init(); let contract = "contracts/create_token_in_constructor/target/ink/create_token_in_constructor.wasm"; - let addr = instantiate_and_create_fungible(contract, ASSET_ID, 1); - assert_eq!(asset_exists(addr, ASSET_ID), Ok(Assets::asset_exists(ASSET_ID))); + // Asset already exists. + create_asset(ALICE, 0, 1); + assert_eq!( + instantiate_and_create_fungible(contract, 0, 1), + Err(Module { index: 52, error: 5 }) + ); + // Successfully create an asset when instantiating the contract. + assert_ok!(instantiate_and_create_fungible(contract, ASSET_ID, 1)); + assert!(Assets::asset_exists(ASSET_ID)); }); } diff --git a/pop-api/integration-tests/src/fungibles/utils.rs b/pop-api/integration-tests/src/fungibles/utils.rs index 555bfd50..c4571aec 100644 --- a/pop-api/integration-tests/src/fungibles/utils.rs +++ b/pop-api/integration-tests/src/fungibles/utils.rs @@ -310,7 +310,7 @@ pub(super) fn instantiate_and_create_fungible( contract: &str, asset_id: AssetId, min_balance: Balance, -) -> AccountId32 { +) -> Result<(), Error> { let function = function_selector("new"); let input = [function, asset_id.encode(), min_balance.encode()].concat(); let (wasm_binary, _) = @@ -327,7 +327,8 @@ pub(super) fn instantiate_and_create_fungible( CollectEvents::Skip, ) .result - .unwrap(); - assert!(!result.result.did_revert(), "deploying contract reverted {:?}", result); - result.account_id + .expect("should work") + .result; + decoded::>(result.clone()) + .unwrap_or_else(|_| panic!("Contract reverted: {:?}", result)) } From 12e1cf9081b5177cfe1b9abe80e55fa618cbac9c Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 18:24:27 +0200 Subject: [PATCH 39/56] test: remove commented out test --- pop-api/integration-tests/src/fungibles/mod.rs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/pop-api/integration-tests/src/fungibles/mod.rs b/pop-api/integration-tests/src/fungibles/mod.rs index e00fb213..31d1c443 100644 --- a/pop-api/integration-tests/src/fungibles/mod.rs +++ b/pop-api/integration-tests/src/fungibles/mod.rs @@ -545,20 +545,3 @@ fn burn_works() { assert_eq!(burn(addr.clone(), asset, BOB, amount), Err(Module { index: 52, error: 16 })); }); } - -// #[test] -// #[ignore] -// fn asset_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!(Assets::asset_exists(ASSET_ID), asset_exists(addr.clone(), ASSET_ID)); -// -// // Tokens in circulation. -// create_asset(addr.clone(), ASSET_ID, 1); -// assert_eq!(Assets::asset_exists(ASSET_ID), asset_exists(addr, ASSET_ID)); -// }); -// } From 68ec0590879bc176fc8b0c4b5cf4e74c9b22e2f7 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 19:29:05 +0200 Subject: [PATCH 40/56] fix: test --- pallets/api/src/fungibles/tests.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index f33e96ef..d6cc87e0 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -217,7 +217,7 @@ fn token_metadata_works() { #[test] fn asset_exists_works() { new_test_ext().execute_with(|| { - create_asset(ALICE, ASSET, 100); + create_asset(ALICE, ASSET); assert_eq!(Assets::asset_exists(ASSET).encode(), Fungibles::read_state(AssetExists(ASSET))); }); } From 0311d9a3cb7565962bb29e11c80646e3273a365d Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 31 Jul 2024 22:41:55 +0200 Subject: [PATCH 41/56] draft api events --- pallets/api/src/fungibles/mod.rs | 167 ++++++++++++++++++++++------- pallets/api/src/mock.rs | 1 + pop-api/src/v0/assets/fungibles.rs | 54 +++++++--- 3 files changed, 169 insertions(+), 53 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 1001663b..3b089a55 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -83,6 +83,8 @@ pub mod pallet { /// Configure the pallet by specifying the parameters and types on which it depends. #[pallet::config] pub trait Config: frame_system::Config + pallet_assets::Config { + /// Because this pallet emits events, it depends on the runtime's definition of an event. + type RuntimeEvent: From> + IsType<::RuntimeEvent>; /// The instance of pallet assets it is tightly coupled to. type AssetsInstance; /// Weight information for dispatchables in this pallet. @@ -92,6 +94,49 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + #[pallet::event] + #[pallet::generate_deposit(pub(super) fn deposit_event)] + pub enum Event { + /// Event emitted when allowance by `owner` to `spender` changes. + Approval { + /// Account providing allowance. + owner: AccountIdOf, + /// Allowance beneficiary. + spender: AccountIdOf, + /// New allowance amount. + value: BalanceOf, + }, + /// Event emitted when transfer of tokens occurs. + Transfer { + /// Transfer sender. `None` in case of minting new tokens. + from: Option>, + /// Transfer recipient. `None` in case of burning tokens. + to: Option>, + /// Amount of tokens transferred (or minted/burned). + value: BalanceOf, + }, + /// Event emitted when a token is created. + Create { + /// Token ID. + id: AssetIdOf, + /// Owner of the token created. + owner: AccountIdOf, + /// Admin of the token created. + admin: AccountIdOf, + }, + /// Event emitted when new token metadata is set. + SetMetadata { + /// Token ID. + id: AssetIdOf, + /// Token name. + name: Vec, + /// Token symbol. + symbol: Vec, + /// Token decimals. + decimals: u8, + }, + } + #[pallet::call] impl Pallet { /// Transfers `value` amount of tokens from the caller's account to account `to`, with additional @@ -106,11 +151,18 @@ pub mod pallet { pub fn transfer( origin: OriginFor, id: AssetIdOf, - target: AccountIdOf, - amount: BalanceOf, + to: AccountIdOf, + value: BalanceOf, ) -> DispatchResult { - let target = T::Lookup::unlookup(target); - AssetsOf::::transfer_keep_alive(origin, id.into(), target, amount) + AssetsOf::::transfer_keep_alive( + origin.clone(), + id.into(), + T::Lookup::unlookup(to.clone()), + value, + )?; + let from = ensure_signed(origin)?; + Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Ok(()) } /// Transfers `value` amount of tokens from the delegated account approved by the `owner` to @@ -118,7 +170,7 @@ pub mod pallet { /// /// # Parameters /// - `id` - The ID of the asset. - /// - `owner` - The account from which the asset balance will be withdrawn. + /// - `from` - The account from which the asset balance will be withdrawn. /// - `to` - The recipient account. /// - `value` - The number of tokens to transfer. #[pallet::call_index(4)] @@ -126,13 +178,19 @@ pub mod pallet { pub fn transfer_from( origin: OriginFor, id: AssetIdOf, - owner: AccountIdOf, - target: AccountIdOf, - amount: BalanceOf, + from: AccountIdOf, + to: AccountIdOf, + value: BalanceOf, ) -> DispatchResult { - let owner = T::Lookup::unlookup(owner); - let target = T::Lookup::unlookup(target); - AssetsOf::::transfer_approved(origin, id.into(), owner, target, amount) + AssetsOf::::transfer_approved( + origin, + id.into(), + T::Lookup::unlookup(from.clone()), + T::Lookup::unlookup(to.clone()), + value, + )?; + Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Ok(()) } /// Approves an account to spend a specified number of tokens on behalf of the caller. @@ -149,10 +207,10 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin.clone()) + let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; - let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); - let spender = T::Lookup::unlookup(spender); + let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); + let spender_unlookup = T::Lookup::unlookup(spender.clone()); let id: AssetIdParameterOf = id.into(); // If the new value is equal to the current allowance, do nothing. @@ -165,21 +223,26 @@ pub mod pallet { AssetsOf::::approve_transfer( origin, id, - spender, + spender_unlookup, value.saturating_sub(current_allowance), ) .map_err(|e| e.with_weight(Self::weight_approve(1, 0)))?; Self::weight_approve(1, 0) } else { // If the new value is less than the current allowance, cancel the approval and set the new value - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) - .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; + AssetsOf::::cancel_approval( + origin.clone(), + id.clone(), + spender_unlookup.clone(), + ) + .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; if value.is_zero() { return Ok(Some(Self::weight_approve(0, 1)).into()); } - AssetsOf::::approve_transfer(origin, id, spender, value)?; + AssetsOf::::approve_transfer(origin, id, spender_unlookup, value)?; Self::weight_approve(1, 1) }; + Self::deposit_event(Event::Approval { owner, spender, value }); Ok(Some(return_weight).into()) } @@ -197,8 +260,16 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - let spender = T::Lookup::unlookup(spender); - AssetsOf::::approve_transfer(origin, id.into(), spender, value) + let owner = ensure_signed(origin.clone())?; + AssetsOf::::approve_transfer( + origin, + id.clone().into(), + T::Lookup::unlookup(spender.clone()), + value, + )?; + let value = AssetsOf::::allowance(id, &owner, &spender); + Self::deposit_event(Event::Approval { owner, spender, value }); + Ok(()) } /// Decreases the allowance of a spender. @@ -215,24 +286,27 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let who = ensure_signed(origin.clone()) + let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; - let current_allowance = AssetsOf::::allowance(id.clone(), &who, &spender); - let spender = T::Lookup::unlookup(spender); + let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); + let spender_unlookup = T::Lookup::unlookup(spender.clone()); let id: AssetIdParameterOf = id.into(); if value.is_zero() { return Ok(Some(Self::weight_approve(0, 0)).into()); } // Cancel the aproval and set the new value if `current_allowance` is more than zero. - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender.clone()) + AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender_unlookup.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; let new_allowance = current_allowance.saturating_sub(value); - if new_allowance.is_zero() { - return Ok(Some(Self::weight_approve(0, 1)).into()); - } - AssetsOf::::approve_transfer(origin, id, spender, new_allowance)?; - Ok(().into()) + let weight = if new_allowance.is_zero() { + Self::weight_approve(0, 1) + } else { + AssetsOf::::approve_transfer(origin, id, spender_unlookup, new_allowance)?; + Self::weight_approve(1, 1) + }; + Self::deposit_event(Event::Approval { owner, spender, value }); + Ok(Some(weight).into()) } /// Create a new token with a given asset ID. @@ -249,8 +323,15 @@ pub mod pallet { admin: AccountIdOf, min_balance: BalanceOf, ) -> DispatchResult { - let admin = T::Lookup::unlookup(admin); - AssetsOf::::create(origin, id.into(), admin, min_balance) + AssetsOf::::create( + origin.clone(), + id.into(), + T::Lookup::unlookup(admin.clone()), + min_balance, + )?; + let owner = ensure_signed(origin)?; + Self::deposit_event(Event::Create { id, owner, admin }); + Ok(()) } /// Start the process of destroying a token with a given asset ID. @@ -281,7 +362,9 @@ pub mod pallet { symbol: Vec, decimals: u8, ) -> DispatchResult { - AssetsOf::::set_metadata(origin, id.into(), name, symbol, decimals) + AssetsOf::::set_metadata(origin, id.into(), name.clone(), symbol.clone(), decimals)?; + Self::deposit_event(Event::SetMetadata { id, name, symbol, decimals }); + Ok(()) } /// Clear the metadata for a token with a given asset ID. @@ -294,11 +377,11 @@ pub mod pallet { AssetsOf::::clear_metadata(origin, id.into()) } - /// Creates `amount` tokens and assigns them to `account`, increasing the total supply. + /// Creates `value` amount of tokens and assigns them to `account`, increasing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. - /// - `owner` - The account to be credited with the created tokens. + /// - `account` - The account to be credited with the created tokens. /// - `value` - The number of tokens to mint. #[pallet::call_index(19)] #[pallet::weight(AssetsWeightInfoOf::::mint())] @@ -306,13 +389,14 @@ pub mod pallet { origin: OriginFor, id: AssetIdOf, account: AccountIdOf, - amount: BalanceOf, + value: BalanceOf, ) -> DispatchResult { - let account = T::Lookup::unlookup(account); - AssetsOf::::mint(origin, id.into(), account, amount) + AssetsOf::::mint(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; + Self::deposit_event(Event::Transfer { from: None, to: Some(account), value }); + Ok(()) } - /// Destroys `amount` tokens from `account`, reducing the total supply. + /// Destroys `value` tokens from `account`, reducing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. @@ -324,10 +408,11 @@ pub mod pallet { origin: OriginFor, id: AssetIdOf, account: AccountIdOf, - amount: BalanceOf, + value: BalanceOf, ) -> DispatchResult { - let account = T::Lookup::unlookup(account); - AssetsOf::::burn(origin, id.into(), account, amount) + AssetsOf::::burn(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; + Self::deposit_event(Event::Transfer { from: Some(account), to: None, value }); + Ok(()) } } diff --git a/pallets/api/src/mock.rs b/pallets/api/src/mock.rs index b20e2635..77e17394 100644 --- a/pallets/api/src/mock.rs +++ b/pallets/api/src/mock.rs @@ -97,6 +97,7 @@ impl pallet_assets::Config for Test { type BenchmarkHelper = (); } impl crate::fungibles::Config for Test { + type RuntimeEvent = RuntimeEvent; type AssetsInstance = AssetsInstance; type WeightInfo = (); } diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 27b96ae9..47e66b84 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -58,6 +58,36 @@ mod constants { pub(super) const BURN: u8 = 20; } +pub mod events { + use super::*; + + /// Event emitted when allowance by `owner` to `spender` changes. + #[ink::event] + pub struct Approval { + /// Account providing allowance. + #[ink(topic)] + pub owner: AccountId, + /// Allowance beneficiary. + #[ink(topic)] + pub spender: AccountId, + /// New allowance amount. + pub value: u128, + } + + /// Event emitted when transfer of tokens occurs. + #[ink::event] + pub struct Transfer { + /// Transfer sender. `None` in case of minting new tokens. + #[ink(topic)] + pub from: Option, + /// Transfer recipient. `None` in case of burning tokens. + #[ink(topic)] + pub to: Option, + /// Amount of tokens transferred (or minted/burned). + pub value: u128, + } +} + /// Returns the total token supply for a given asset ID. /// /// # Parameters @@ -122,12 +152,12 @@ pub fn allowance(id: AssetId, owner: AccountId, spender: AccountId) -> Result Result<()> { +pub fn transfer(id: AssetId, to: AccountId, value: Balance) -> Result<()> { build_dispatch(TRANSFER) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, target, amount)) + .call(&(id, to, value)) } /// Transfers `value` tokens on behalf of `from` to account `to` with additional `data` @@ -143,12 +173,12 @@ pub fn transfer(id: AssetId, target: AccountId, amount: Balance) -> Result<()> { /// # Returns /// 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<()> { +pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, value: Balance) -> Result<()> { build_dispatch(TRANSFER_FROM) .input::<(AssetId, AccountId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, from, to, amount)) + .call(&(id, from, to, value)) } /// Approves an account to spend a specified number of tokens on behalf of the caller. @@ -161,12 +191,12 @@ pub fn transfer_from(id: AssetId, from: AccountId, to: AccountId, amount: Balanc /// # Returns /// Returns `Ok(())` if successful, or an error if the approval fails. #[inline] -pub fn approve(id: AssetId, spender: AccountId, amount: Balance) -> Result<()> { +pub fn approve(id: AssetId, spender: AccountId, value: Balance) -> Result<()> { build_dispatch(APPROVE) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, spender, amount)) + .call(&(id, spender, value)) } /// Increases the allowance of a spender. @@ -205,7 +235,7 @@ pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re .call(&(id, spender, value)) } -/// Creates `amount` tokens and assigns them to `account`, increasing the total supply. +/// Creates `value` tokens and assigns them to `account`, increasing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. @@ -214,15 +244,15 @@ pub fn decrease_allowance(id: AssetId, spender: AccountId, value: Balance) -> Re /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. -pub fn mint(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { +pub fn mint(id: AssetId, account: AccountId, value: Balance) -> Result<()> { build_dispatch(MINT) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, account, amount)) + .call(&(id, account, value)) } -/// Destroys `amount` tokens from `account`, reducing the total supply. +/// Destroys `value` tokens from `account`, reducing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. @@ -231,12 +261,12 @@ pub fn mint(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { /// /// # Returns /// Returns `Ok(())` if successful, or an error if the operation fails. -pub fn burn(id: AssetId, account: AccountId, amount: Balance) -> Result<()> { +pub fn burn(id: AssetId, account: AccountId, value: Balance) -> Result<()> { build_dispatch(BURN) .input::<(AssetId, AccountId, Balance)>() .output::, true>() .handle_error_code::() - .call(&(id, account, amount)) + .call(&(id, account, value)) } pub mod metadata { From 3a777d513badbc8e55e0216ee2ed95974bbee562 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 6 Aug 2024 18:12:30 +0200 Subject: [PATCH 42/56] feat: events to all features + tests --- pallets/api/src/fungibles/mod.rs | 145 ++++++++++++-------- pallets/api/src/fungibles/tests.rs | 210 ++++++++++++++++++----------- 2 files changed, 224 insertions(+), 131 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 6648b038..6334ee47 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -100,6 +100,8 @@ pub mod pallet { pub enum Event { /// Event emitted when allowance by `owner` to `spender` changes. Approval { + /// Token ID. + id: AssetIdOf, /// Account providing allowance. owner: AccountIdOf, /// Allowance beneficiary. @@ -109,6 +111,8 @@ pub mod pallet { }, /// Event emitted when transfer of tokens occurs. Transfer { + /// Token ID. + id: AssetIdOf, /// Transfer sender. `None` in case of minting new tokens. from: Option>, /// Transfer recipient. `None` in case of burning tokens. @@ -125,7 +129,9 @@ pub mod pallet { /// Admin of the token created. admin: AccountIdOf, }, - /// Event emitted when new token metadata is set. + /// Event emitted when a token is in the process of being destroyed. + StartDestroy { id: AssetIdOf }, + /// Event emitted when new metadata is set for a token. SetMetadata { /// Token ID. id: AssetIdOf, @@ -136,6 +142,8 @@ pub mod pallet { /// Token decimals. decimals: u8, }, + /// Event emitted when metadata is cleared for a token. + ClearMetadata { id: AssetIdOf }, } #[pallet::call] @@ -157,12 +165,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::transfer_keep_alive( origin.clone(), - id.into(), + id.clone().into(), T::Lookup::unlookup(to.clone()), value, )?; let from = ensure_signed(origin)?; - Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Self::deposit_event(Event::Transfer { id, from: Some(from), to: Some(to), value }); Ok(()) } @@ -185,12 +193,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::transfer_approved( origin, - id.into(), + id.clone().into(), T::Lookup::unlookup(from.clone()), T::Lookup::unlookup(to.clone()), value, )?; - Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Self::deposit_event(Event::Transfer { id, from: Some(from), to: Some(to), value }); Ok(()) } @@ -208,40 +216,45 @@ pub mod pallet { spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let owner = ensure_signed(origin.clone()) - .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; - let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); - let spender_unlookup = T::Lookup::unlookup(spender.clone()); - let id: AssetIdParameterOf = id.into(); + let owner = ensure_signed(origin.clone()) + .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; + let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); + let spender_unlookup = T::Lookup::unlookup(spender.clone()); + let id_param: AssetIdParameterOf = id.clone().into(); - let return_weight = match value.cmp(¤t_allowance) { - // If the new value is equal to the current allowance, do nothing. - Equal => Self::weight_approve(0, 0), - // If the new value is greater than the current allowance, approve the difference - // because `approve_transfer` works additively (see `pallet-assets`). - Greater => { - AssetsOf::::approve_transfer( - origin, - id, - spender_unlookup, - value.saturating_sub(current_allowance), - ) - .map_err(|e| e.with_weight(Self::weight_approve(1, 0)))?; - Self::weight_approve(1, 0) - }, - // If the new value is less than the current allowance, cancel the approval and - // set the new value. - Less => { - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender_unlookup.clone()) - .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; - if value.is_zero() { - return Ok(Some(Self::weight_approve(0, 1)).into()); - } - AssetsOf::::approve_transfer(origin, id, spender_unlookup, value)?; - Self::weight_approve(1, 1) - }, - } - Self::deposit_event(Event::Approval { owner, spender, value }); + let return_weight = match value.cmp(¤t_allowance) { + // If the new value is equal to the current allowance, do nothing. + Equal => Self::weight_approve(0, 0), + // If the new value is greater than the current allowance, approve the difference + // because `approve_transfer` works additively (see `pallet-assets`). + Greater => { + AssetsOf::::approve_transfer( + origin, + id_param, + spender_unlookup, + value.saturating_sub(current_allowance), + ) + .map_err(|e| e.with_weight(Self::weight_approve(1, 0)))?; + Self::weight_approve(1, 0) + }, + // If the new value is less than the current allowance, cancel the approval and + // set the new value. + Less => { + AssetsOf::::cancel_approval( + origin.clone(), + id_param.clone(), + spender_unlookup.clone(), + ) + .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; + if value.is_zero() { + Self::weight_approve(0, 1) + } else { + AssetsOf::::approve_transfer(origin, id_param, spender_unlookup, value)?; + Self::weight_approve(1, 1) + } + }, + }; + Self::deposit_event(Event::Approval { id, owner, spender, value }); Ok(Some(return_weight).into()) } @@ -266,8 +279,8 @@ pub mod pallet { T::Lookup::unlookup(spender.clone()), value, )?; - let value = AssetsOf::::allowance(id, &owner, &spender); - Self::deposit_event(Event::Approval { owner, spender, value }); + let value = AssetsOf::::allowance(id.clone(), &owner, &spender); + Self::deposit_event(Event::Approval { id, owner, spender, value }); Ok(()) } @@ -289,22 +302,26 @@ pub mod pallet { .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); let spender_unlookup = T::Lookup::unlookup(spender.clone()); - let id: AssetIdParameterOf = id.into(); + let id_param: AssetIdParameterOf = id.clone().into(); if value.is_zero() { return Ok(Some(Self::weight_approve(0, 0)).into()); } - // Cancel the aproval and set the new value if `new_allowance` is more than zero. - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender_unlookup.clone()) - .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; + // Cancel the approval and set the new value if `new_allowance` is more than zero. + AssetsOf::::cancel_approval( + origin.clone(), + id_param.clone(), + spender_unlookup.clone(), + ) + .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; let new_allowance = current_allowance.saturating_sub(value); let weight = if new_allowance.is_zero() { Self::weight_approve(0, 1) } else { - AssetsOf::::approve_transfer(origin, id, spender_unlookup, new_allowance)?; + AssetsOf::::approve_transfer(origin, id_param, spender_unlookup, new_allowance)?; Self::weight_approve(1, 1) }; - Self::deposit_event(Event::Approval { owner, spender, value }); + Self::deposit_event(Event::Approval { id, owner, spender, value: new_allowance }); Ok(Some(weight).into()) } @@ -324,7 +341,7 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::create( origin.clone(), - id.into(), + id.clone().into(), T::Lookup::unlookup(admin.clone()), min_balance, )?; @@ -340,7 +357,9 @@ pub mod pallet { #[pallet::call_index(12)] #[pallet::weight(AssetsWeightInfoOf::::start_destroy())] pub fn start_destroy(origin: OriginFor, id: AssetIdOf) -> DispatchResult { - AssetsOf::::start_destroy(origin, id.into()) + AssetsOf::::start_destroy(origin, id.clone().into())?; + Self::deposit_event(Event::StartDestroy { id }); + Ok(()) } /// Set the metadata for a token with a given asset ID. @@ -361,7 +380,13 @@ pub mod pallet { symbol: Vec, decimals: u8, ) -> DispatchResult { - AssetsOf::::set_metadata(origin, id.into(), name.clone(), symbol.clone(), decimals)?; + AssetsOf::::set_metadata( + origin, + id.clone().into(), + name.clone(), + symbol.clone(), + decimals, + )?; Self::deposit_event(Event::SetMetadata { id, name, symbol, decimals }); Ok(()) } @@ -373,7 +398,9 @@ pub mod pallet { #[pallet::call_index(17)] #[pallet::weight(AssetsWeightInfoOf::::clear_metadata())] pub fn clear_metadata(origin: OriginFor, id: AssetIdOf) -> DispatchResult { - AssetsOf::::clear_metadata(origin, id.into()) + AssetsOf::::clear_metadata(origin, id.clone().into())?; + Self::deposit_event(Event::ClearMetadata { id }); + Ok(()) } /// Creates `value` amount of tokens and assigns them to `account`, increasing the total supply. @@ -390,8 +417,13 @@ pub mod pallet { account: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - AssetsOf::::mint(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; - Self::deposit_event(Event::Transfer { from: None, to: Some(account), value }); + AssetsOf::::mint( + origin, + id.clone().into(), + T::Lookup::unlookup(account.clone()), + value, + )?; + Self::deposit_event(Event::Transfer { id, from: None, to: Some(account), value }); Ok(()) } @@ -409,8 +441,13 @@ pub mod pallet { account: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - AssetsOf::::burn(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; - Self::deposit_event(Event::Transfer { from: Some(account), to: None, value }); + AssetsOf::::burn( + origin, + id.clone().into(), + T::Lookup::unlookup(account.clone()), + value, + )?; + Self::deposit_event(Event::Transfer { id, from: Some(account), to: None, value }); Ok(()) } } diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index d6cc87e0..23147d42 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -9,34 +9,45 @@ use frame_support::{ const ASSET: u32 = 42; +type Event = crate::fungibles::Event; + #[test] fn transfer_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); - let balance_before_transfer = Assets::balance(ASSET, &BOB); - assert_ok!(Fungibles::transfer(signed(ALICE), ASSET, BOB, amount / 2)); - let balance_after_transfer = Assets::balance(ASSET, &BOB); - assert_eq!(balance_after_transfer, balance_before_transfer + amount / 2); + let value: Balance = 100 * UNIT; + let id = ASSET; + let from = Some(ALICE); + let to = Some(BOB); + + create_asset_and_mint_to(ALICE, id, ALICE, value * 2); + let balance_before_transfer = Assets::balance(id, &BOB); + assert_ok!(Fungibles::transfer(signed(ALICE), id, BOB, value)); + let balance_after_transfer = Assets::balance(id, &BOB); + assert_eq!(balance_after_transfer, balance_before_transfer + value); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } #[test] fn transfer_from_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - // Approve CHARLIE to transfer up to `amount` to BOB. - create_asset_mint_and_approve(ALICE, ASSET, ALICE, amount * 2, CHARLIE, amount / 2); - let transferred = amount / 2; + let value: Balance = 100 * UNIT; + let id = ASSET; + let from = Some(ALICE); + let to = Some(BOB); + + // Approve CHARLIE to transfer up to `value` to BOB. + create_asset_mint_and_approve(ALICE, id, ALICE, value * 2, CHARLIE, value); // Successfully call transfer from. - let alice_balance_before_transfer = Assets::balance(ASSET, &ALICE); - let balance_before_transfer = Assets::balance(ASSET, &BOB); - assert_ok!(Fungibles::transfer_from(signed(CHARLIE), ASSET, ALICE, BOB, transferred)); - let alice_balance_after_transfer = Assets::balance(ASSET, &ALICE); - let balance_after_transfer = Assets::balance(ASSET, &BOB); - // Check that BOB receives the `amount` and ALICE `amount` is spent successfully by CHARLIE. - assert_eq!(balance_after_transfer, balance_before_transfer + transferred); - assert_eq!(alice_balance_after_transfer, alice_balance_before_transfer - transferred); + let alice_balance_before_transfer = Assets::balance(id, &ALICE); + let bob_balance_before_transfer = Assets::balance(id, &BOB); + assert_ok!(Fungibles::transfer_from(signed(CHARLIE), id, ALICE, BOB, value)); + let alice_balance_after_transfer = Assets::balance(id, &ALICE); + let bob_balance_after_transfer = Assets::balance(id, &BOB); + // Check that BOB receives the `value` and ALICE `amount` is spent successfully by CHARLIE. + assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + value); + assert_eq!(alice_balance_after_transfer, alice_balance_before_transfer - value); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } @@ -44,130 +55,175 @@ fn transfer_from_works() { #[test] fn approve_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); - assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); - assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); - // Approves an amount to spend that is lower than the current allowance. - assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount / 2)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount / 2); - // Approves an amount to spend that is higher than the current allowance. - assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); - // Approves an amount to spend that is equal to the current allowance. - assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, amount * 2)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + let value: Balance = 100 * UNIT; + let id = ASSET; + let owner = ALICE; + let spender = BOB; + + create_asset_and_mint_to(ALICE, id, ALICE, value); + assert_eq!(0, Assets::allowance(id, &ALICE, &BOB)); + assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); + System::assert_last_event(Event::Approval { id, owner, spender, value }.into()); + // Approves an value to spend that is lower than the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value / 2)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value / 2); + System::assert_last_event(Event::Approval { id, owner, spender, value: value / 2 }.into()); + // Approves an value to spend that is higher than the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value * 2)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); + System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); + // Approves an value to spend that is equal to the current allowance. + assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value * 2)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); + System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); // Sets allowance to zero. - assert_ok!(Fungibles::approve(signed(ALICE), ASSET, BOB, 0)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), 0); + assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, 0)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), 0); + System::assert_last_event(Event::Approval { id, owner, spender, value: 0 }.into()); }); } #[test] fn increase_allowance_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - create_asset_and_mint_to(ALICE, ASSET, ALICE, amount); - assert_eq!(0, Assets::allowance(ASSET, &ALICE, &BOB)); - assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + let value: Balance = 100 * UNIT; + let id = ASSET; + let owner = ALICE; + let spender = BOB; + + create_asset_and_mint_to(ALICE, id, ALICE, value); + assert_eq!(0, Assets::allowance(id, &ALICE, &BOB)); + assert_ok!(Fungibles::increase_allowance(signed(ALICE), id, BOB, value)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); + System::assert_last_event(Event::Approval { id, owner, spender, value }.into()); // Additive. - assert_ok!(Fungibles::increase_allowance(signed(ALICE), ASSET, BOB, amount)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount * 2); + assert_ok!(Fungibles::increase_allowance(signed(ALICE), id, BOB, value)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); + System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); }); } #[test] fn decrease_allowance_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - create_asset_mint_and_approve(ALICE, ASSET, ALICE, amount, BOB, amount); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + let value: Balance = 100 * UNIT; + let id = ASSET; + let owner = ALICE; + let spender = BOB; + + create_asset_mint_and_approve(ALICE, id, ALICE, value, BOB, value); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); // Owner balance is not changed if decreased by zero. - assert_ok!(Fungibles::decrease_allowance(signed(ALICE), ASSET, BOB, 0)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount); + assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, 0)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); // Decrease allowance successfully. - assert_ok!(Fungibles::decrease_allowance(signed(ALICE), ASSET, BOB, amount / 2 - 1 * UNIT)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), amount / 2 + 1 * UNIT); + assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, value / 2)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), value / 2); + System::assert_last_event(Event::Approval { id, owner, spender, value: value / 2 }.into()); // Saturating if current allowance is decreased more than the owner balance. - assert_ok!(Fungibles::decrease_allowance(signed(ALICE), ASSET, BOB, amount)); - assert_eq!(Assets::allowance(ASSET, &ALICE, &BOB), 0); + assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, value)); + assert_eq!(Assets::allowance(id, &ALICE, &BOB), 0); + System::assert_last_event(Event::Approval { id, owner, spender, value: 0 }.into()); }); } #[test] fn create_works() { new_test_ext().execute_with(|| { - assert!(!Assets::asset_exists(ASSET)); - assert_ok!(Fungibles::create(signed(ALICE), ASSET, ALICE, 100)); - assert!(Assets::asset_exists(ASSET)); + let id = ASSET; + let owner = ALICE; + let admin = ALICE; + + assert!(!Assets::asset_exists(id)); + assert_ok!(Fungibles::create(signed(ALICE), id, ALICE, 100)); + assert!(Assets::asset_exists(id)); + System::assert_last_event(Event::Create { id, owner, admin }.into()); }); } #[test] fn start_destroy_works() { new_test_ext().execute_with(|| { - create_asset(ALICE, ASSET); - assert_ok!(Fungibles::start_destroy(signed(ALICE), ASSET)); + let id = ASSET; + + create_asset(ALICE, id); + assert_ok!(Fungibles::start_destroy(signed(ALICE), id)); + System::assert_last_event(Event::StartDestroy { id }.into()); }); } #[test] fn set_metadata_works() { new_test_ext().execute_with(|| { + let id = ASSET; let name = vec![42]; let symbol = vec![42]; let decimals = 42; - create_asset(ALICE, ASSET); + + create_asset(ALICE, id); assert_ok!(Fungibles::set_metadata( signed(ALICE), - ASSET, + id, name.clone(), symbol.clone(), decimals )); - assert_eq!(Assets::name(ASSET), name); - assert_eq!(Assets::symbol(ASSET), symbol); - assert_eq!(Assets::decimals(ASSET), decimals); + assert_eq!(Assets::name(id), name); + assert_eq!(Assets::symbol(id), symbol); + assert_eq!(Assets::decimals(id), decimals); + System::assert_last_event(Event::SetMetadata { id, name, symbol, decimals }.into()); }); } #[test] fn clear_metadata_works() { new_test_ext().execute_with(|| { + let id = ASSET; let name = vec![42]; let symbol = vec![42]; let decimals = 42; - create_asset_and_set_metadata(ALICE, ASSET, name, symbol, decimals); - assert_ok!(Fungibles::clear_metadata(signed(ALICE), ASSET)); - assert_eq!(Assets::name(ASSET), Vec::::new()); - assert_eq!(Assets::symbol(ASSET), Vec::::new()); - assert_eq!(Assets::decimals(ASSET), 0u8); + + create_asset_and_set_metadata(ALICE, id, name, symbol, decimals); + assert_ok!(Fungibles::clear_metadata(signed(ALICE), id)); + assert_eq!(Assets::name(id), Vec::::new()); + assert_eq!(Assets::symbol(id), Vec::::new()); + assert_eq!(Assets::decimals(id), 0u8); + System::assert_last_event(Event::ClearMetadata { id }.into()); }); } #[test] fn mint_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - create_asset(ALICE, ASSET); - let balance_before_mint = Assets::balance(ASSET, &BOB); - assert_ok!(Fungibles::mint(signed(ALICE), ASSET, BOB, amount)); - let balance_after_mint = Assets::balance(ASSET, &BOB); - assert_eq!(balance_after_mint, balance_before_mint + amount); + let value: Balance = 100 * UNIT; + let id = ASSET; + let from = None; + let to = Some(BOB); + + create_asset(ALICE, id); + let balance_before_mint = Assets::balance(id, &BOB); + assert_ok!(Fungibles::mint(signed(ALICE), id, BOB, value)); + let balance_after_mint = Assets::balance(id, &BOB); + assert_eq!(balance_after_mint, balance_before_mint + value); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } #[test] fn burn_works() { new_test_ext().execute_with(|| { - let amount: Balance = 100 * UNIT; - create_asset_and_mint_to(ALICE, ASSET, BOB, amount); - let balance_before_burn = Assets::balance(ASSET, &BOB); - assert_ok!(Fungibles::burn(signed(ALICE), ASSET, BOB, amount)); - let balance_after_burn = Assets::balance(ASSET, &BOB); - assert_eq!(balance_after_burn, balance_before_burn - amount); + let value: Balance = 100 * UNIT; + let id = ASSET; + let from = Some(BOB); + let to = None; + + create_asset_and_mint_to(ALICE, id, BOB, value); + let balance_before_burn = Assets::balance(id, &BOB); + assert_ok!(Fungibles::burn(signed(ALICE), id, BOB, value)); + let balance_after_burn = Assets::balance(id, &BOB); + assert_eq!(balance_after_burn, balance_before_burn - value); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } From bc8181cf3b833405df2cc278b94c4e899750a82b Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 6 Aug 2024 19:00:34 +0200 Subject: [PATCH 43/56] feat (api): events fungibles --- pop-api/src/v0/assets/fungibles.rs | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index e03e6c1f..87499468 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -86,6 +86,35 @@ pub mod events { /// Amount of tokens transferred (or minted/burned). pub value: u128, } + + /// Event emitted when a token class is created. + #[ink::event] + pub struct Create { + id: AssetId, + creator: AccountId, + owner: AccountId, + } + + /// Event emitted when a token is in the process of being destroyed. + #[ink::event] + pub struct StartDestroy { + id: AssetId, + } + + /// Event emitted when new metadata is set for a token. + #[ink::event] + pub struct SetMetadata { + id: AssetId, + name: Vec, + symbol: Vec, + decimals: u8, + } + + /// Event emitted when metadata is cleared for a token. + #[ink::event] + pub struct ClearMetadata { + id: AssetId, + } } /// Returns the total token supply for a given asset ID. From 6b10f2a693ec97087650467d28a56ee9378dcf11 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 7 Aug 2024 12:20:32 +0200 Subject: [PATCH 44/56] fix: compilation --- pallets/api/src/fungibles/mod.rs | 2 +- runtime/devnet/src/config/api.rs | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 6334ee47..e525bc81 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -427,7 +427,7 @@ pub mod pallet { Ok(()) } - /// Destroys `value` tokens from `account`, reducing the total supply. + /// Destroys `value` amount of tokens from `account`, reducing the total supply. /// /// # Parameters /// - `id` - The ID of the asset. diff --git a/runtime/devnet/src/config/api.rs b/runtime/devnet/src/config/api.rs index bdff6bbb..34f96b5a 100644 --- a/runtime/devnet/src/config/api.rs +++ b/runtime/devnet/src/config/api.rs @@ -1,4 +1,6 @@ -use crate::{config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall}; +use crate::{ + config::assets::TrustBackedAssetsInstance, fungibles, Runtime, RuntimeCall, RuntimeEvent, +}; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::traits::Contains; @@ -51,6 +53,7 @@ impl Contains> for AllowedApiCalls { } impl fungibles::Config for Runtime { + type RuntimeEvent = RuntimeEvent; type AssetsInstance = TrustBackedAssetsInstance; type WeightInfo = fungibles::weights::SubstrateWeight; } From 626b3eb49d4fd360615b6fdc0b878f730b6d797c Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 7 Aug 2024 12:34:26 +0200 Subject: [PATCH 45/56] fix: weight increase_allowance --- pallets/api/src/fungibles/mod.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index e525bc81..00ac8761 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -265,23 +265,24 @@ pub mod pallet { /// - `spender` - The account that is allowed to spend the tokens. /// - `value` - The number of tokens to increase the allowance by. #[pallet::call_index(6)] - #[pallet::weight(AssetsWeightInfoOf::::approve_transfer())] + #[pallet::weight(AssetsWeightInfoOf::::approve_transfer() + T::DbWeight::get().reads(1))] pub fn increase_allowance( origin: OriginFor, id: AssetIdOf, spender: AccountIdOf, value: BalanceOf, - ) -> DispatchResult { + ) -> DispatchResultWithPostInfo { let owner = ensure_signed(origin.clone())?; AssetsOf::::approve_transfer( origin, id.clone().into(), T::Lookup::unlookup(spender.clone()), value, - )?; + ) + .map_err(|e| e.with_weight(AssetsWeightInfoOf::::approve_transfer()))?; let value = AssetsOf::::allowance(id.clone(), &owner, &spender); Self::deposit_event(Event::Approval { id, owner, spender, value }); - Ok(()) + Ok(().into()) } /// Decreases the allowance of a spender. From 7c0b3fdcad596aeb6bf500ec3f53041b938cc5a4 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 12 Aug 2024 09:51:18 +0200 Subject: [PATCH 46/56] refactor: remove duplicated events --- pallets/api/src/fungibles/mod.rs | 52 +++--------------------------- pallets/api/src/fungibles/tests.rs | 11 +------ 2 files changed, 5 insertions(+), 58 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 00ac8761..4dc93138 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -120,30 +120,6 @@ pub mod pallet { /// Amount of tokens transferred (or minted/burned). value: BalanceOf, }, - /// Event emitted when a token is created. - Create { - /// Token ID. - id: AssetIdOf, - /// Owner of the token created. - owner: AccountIdOf, - /// Admin of the token created. - admin: AccountIdOf, - }, - /// Event emitted when a token is in the process of being destroyed. - StartDestroy { id: AssetIdOf }, - /// Event emitted when new metadata is set for a token. - SetMetadata { - /// Token ID. - id: AssetIdOf, - /// Token name. - name: Vec, - /// Token symbol. - symbol: Vec, - /// Token decimals. - decimals: u8, - }, - /// Event emitted when metadata is cleared for a token. - ClearMetadata { id: AssetIdOf }, } #[pallet::call] @@ -340,15 +316,7 @@ pub mod pallet { admin: AccountIdOf, min_balance: BalanceOf, ) -> DispatchResult { - AssetsOf::::create( - origin.clone(), - id.clone().into(), - T::Lookup::unlookup(admin.clone()), - min_balance, - )?; - let owner = ensure_signed(origin)?; - Self::deposit_event(Event::Create { id, owner, admin }); - Ok(()) + AssetsOf::::create(origin, id.into(), T::Lookup::unlookup(admin), min_balance) } /// Start the process of destroying a token with a given asset ID. @@ -358,9 +326,7 @@ pub mod pallet { #[pallet::call_index(12)] #[pallet::weight(AssetsWeightInfoOf::::start_destroy())] pub fn start_destroy(origin: OriginFor, id: AssetIdOf) -> DispatchResult { - AssetsOf::::start_destroy(origin, id.clone().into())?; - Self::deposit_event(Event::StartDestroy { id }); - Ok(()) + AssetsOf::::start_destroy(origin, id.into()) } /// Set the metadata for a token with a given asset ID. @@ -381,15 +347,7 @@ pub mod pallet { symbol: Vec, decimals: u8, ) -> DispatchResult { - AssetsOf::::set_metadata( - origin, - id.clone().into(), - name.clone(), - symbol.clone(), - decimals, - )?; - Self::deposit_event(Event::SetMetadata { id, name, symbol, decimals }); - Ok(()) + AssetsOf::::set_metadata(origin, id.into(), name, symbol, decimals) } /// Clear the metadata for a token with a given asset ID. @@ -399,9 +357,7 @@ pub mod pallet { #[pallet::call_index(17)] #[pallet::weight(AssetsWeightInfoOf::::clear_metadata())] pub fn clear_metadata(origin: OriginFor, id: AssetIdOf) -> DispatchResult { - AssetsOf::::clear_metadata(origin, id.clone().into())?; - Self::deposit_event(Event::ClearMetadata { id }); - Ok(()) + AssetsOf::::clear_metadata(origin, id.into()) } /// Creates `value` amount of tokens and assigns them to `account`, increasing the total supply. diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 23147d42..a77d7e60 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -132,13 +132,10 @@ fn decrease_allowance_works() { fn create_works() { new_test_ext().execute_with(|| { let id = ASSET; - let owner = ALICE; - let admin = ALICE; assert!(!Assets::asset_exists(id)); assert_ok!(Fungibles::create(signed(ALICE), id, ALICE, 100)); assert!(Assets::asset_exists(id)); - System::assert_last_event(Event::Create { id, owner, admin }.into()); }); } @@ -149,7 +146,6 @@ fn start_destroy_works() { create_asset(ALICE, id); assert_ok!(Fungibles::start_destroy(signed(ALICE), id)); - System::assert_last_event(Event::StartDestroy { id }.into()); }); } @@ -172,7 +168,6 @@ fn set_metadata_works() { assert_eq!(Assets::name(id), name); assert_eq!(Assets::symbol(id), symbol); assert_eq!(Assets::decimals(id), decimals); - System::assert_last_event(Event::SetMetadata { id, name, symbol, decimals }.into()); }); } @@ -180,16 +175,12 @@ fn set_metadata_works() { fn clear_metadata_works() { new_test_ext().execute_with(|| { let id = ASSET; - let name = vec![42]; - let symbol = vec![42]; - let decimals = 42; - create_asset_and_set_metadata(ALICE, id, name, symbol, decimals); + create_asset_and_set_metadata(ALICE, id, vec![42], vec![42], 42); assert_ok!(Fungibles::clear_metadata(signed(ALICE), id)); assert_eq!(Assets::name(id), Vec::::new()); assert_eq!(Assets::symbol(id), Vec::::new()); assert_eq!(Assets::decimals(id), 0u8); - System::assert_last_event(Event::ClearMetadata { id }.into()); }); } From 9862e1952aff7df45f6e51e4dc33f6604814ca07 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 12 Aug 2024 17:45:05 +0200 Subject: [PATCH 47/56] refactor: ink topics + docs to contract events --- pop-api/src/v0/assets/fungibles.rs | 40 ++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 11 deletions(-) diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 87499468..6905842d 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -90,30 +90,48 @@ pub mod events { /// Event emitted when a token class is created. #[ink::event] pub struct Create { - id: AssetId, - creator: AccountId, - owner: AccountId, + /// The ID of the asset. + #[ink(topic)] + pub id: AssetId, + /// Creator of the asset. + #[ink(topic)] + pub creator: AccountId, + /// Admin of the asset. + #[ink(topic)] + pub admin: AccountId, } - /// Event emitted when a token is in the process of being destroyed. + /// Event emitted when a asset is in the process of being destroyed. #[ink::event] pub struct StartDestroy { - id: AssetId, + /// The ID of the asset. + #[ink(topic)] + pub id: AssetId, } - /// Event emitted when new metadata is set for a token. + /// Event emitted when new metadata is set for an asset. #[ink::event] pub struct SetMetadata { - id: AssetId, - name: Vec, - symbol: Vec, - decimals: u8, + /// The ID of the asset created. + #[ink(topic)] + pub id: AssetId, + #[ink(topic)] + /// The name of the asset. + pub name: Vec, + /// The symbol of the asset. + #[ink(topic)] + pub symbol: Vec, + /// The decimals of the asset. + #[ink(topic)] + pub decimals: u8, } /// Event emitted when metadata is cleared for a token. #[ink::event] pub struct ClearMetadata { - id: AssetId, + /// The ID of the asset. + #[ink(topic)] + pub id: AssetId, } } From 28977a0d19aa31290224139222a6bc0802e91374 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 12 Aug 2024 17:57:35 +0200 Subject: [PATCH 48/56] fix: incompatible event based on standard --- pallets/api/src/fungibles/mod.rs | 43 +++++++++++------------------- pallets/api/src/fungibles/tests.rs | 26 +++++++++--------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 4dc93138..e591ebb0 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -95,13 +95,12 @@ pub mod pallet { #[pallet::pallet] pub struct Pallet(_); + /// The events that can be emitted. #[pallet::event] #[pallet::generate_deposit(pub(super) fn deposit_event)] pub enum Event { /// Event emitted when allowance by `owner` to `spender` changes. Approval { - /// Token ID. - id: AssetIdOf, /// Account providing allowance. owner: AccountIdOf, /// Allowance beneficiary. @@ -111,8 +110,6 @@ pub mod pallet { }, /// Event emitted when transfer of tokens occurs. Transfer { - /// Token ID. - id: AssetIdOf, /// Transfer sender. `None` in case of minting new tokens. from: Option>, /// Transfer recipient. `None` in case of burning tokens. @@ -141,12 +138,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::transfer_keep_alive( origin.clone(), - id.clone().into(), + id.into(), T::Lookup::unlookup(to.clone()), value, )?; let from = ensure_signed(origin)?; - Self::deposit_event(Event::Transfer { id, from: Some(from), to: Some(to), value }); + Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); Ok(()) } @@ -169,12 +166,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::transfer_approved( origin, - id.clone().into(), + id.into(), T::Lookup::unlookup(from.clone()), T::Lookup::unlookup(to.clone()), value, )?; - Self::deposit_event(Event::Transfer { id, from: Some(from), to: Some(to), value }); + Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); Ok(()) } @@ -196,7 +193,7 @@ pub mod pallet { .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); let spender_unlookup = T::Lookup::unlookup(spender.clone()); - let id_param: AssetIdParameterOf = id.clone().into(); + let id_param: AssetIdParameterOf = id.into(); let return_weight = match value.cmp(¤t_allowance) { // If the new value is equal to the current allowance, do nothing. @@ -230,7 +227,7 @@ pub mod pallet { } }, }; - Self::deposit_event(Event::Approval { id, owner, spender, value }); + Self::deposit_event(Event::Approval { owner, spender, value }); Ok(Some(return_weight).into()) } @@ -256,8 +253,8 @@ pub mod pallet { value, ) .map_err(|e| e.with_weight(AssetsWeightInfoOf::::approve_transfer()))?; - let value = AssetsOf::::allowance(id.clone(), &owner, &spender); - Self::deposit_event(Event::Approval { id, owner, spender, value }); + let value = AssetsOf::::allowance(id, &owner, &spender); + Self::deposit_event(Event::Approval { owner, spender, value }); Ok(().into()) } @@ -279,7 +276,7 @@ pub mod pallet { .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); let spender_unlookup = T::Lookup::unlookup(spender.clone()); - let id_param: AssetIdParameterOf = id.clone().into(); + let id_param: AssetIdParameterOf = id.into(); if value.is_zero() { return Ok(Some(Self::weight_approve(0, 0)).into()); @@ -298,7 +295,7 @@ pub mod pallet { AssetsOf::::approve_transfer(origin, id_param, spender_unlookup, new_allowance)?; Self::weight_approve(1, 1) }; - Self::deposit_event(Event::Approval { id, owner, spender, value: new_allowance }); + Self::deposit_event(Event::Approval { owner, spender, value: new_allowance }); Ok(Some(weight).into()) } @@ -374,13 +371,8 @@ pub mod pallet { account: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - AssetsOf::::mint( - origin, - id.clone().into(), - T::Lookup::unlookup(account.clone()), - value, - )?; - Self::deposit_event(Event::Transfer { id, from: None, to: Some(account), value }); + AssetsOf::::mint(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; + Self::deposit_event(Event::Transfer { from: None, to: Some(account), value }); Ok(()) } @@ -398,13 +390,8 @@ pub mod pallet { account: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - AssetsOf::::burn( - origin, - id.clone().into(), - T::Lookup::unlookup(account.clone()), - value, - )?; - Self::deposit_event(Event::Transfer { id, from: Some(account), to: None, value }); + AssetsOf::::burn(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; + Self::deposit_event(Event::Transfer { from: Some(account), to: None, value }); Ok(()) } } diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index a77d7e60..a30aaa2f 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -24,7 +24,7 @@ fn transfer_works() { assert_ok!(Fungibles::transfer(signed(ALICE), id, BOB, value)); let balance_after_transfer = Assets::balance(id, &BOB); assert_eq!(balance_after_transfer, balance_before_transfer + value); - System::assert_last_event(Event::Transfer { id, from, to, value }.into()); + System::assert_last_event(Event::Transfer { from, to, value }.into()); }); } @@ -47,7 +47,7 @@ fn transfer_from_works() { // Check that BOB receives the `value` and ALICE `amount` is spent successfully by CHARLIE. assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + value); assert_eq!(alice_balance_after_transfer, alice_balance_before_transfer - value); - System::assert_last_event(Event::Transfer { id, from, to, value }.into()); + System::assert_last_event(Event::Transfer { from, to, value }.into()); }); } @@ -64,23 +64,23 @@ fn approve_works() { assert_eq!(0, Assets::allowance(id, &ALICE, &BOB)); assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); - System::assert_last_event(Event::Approval { id, owner, spender, value }.into()); + System::assert_last_event(Event::Approval { owner, spender, value }.into()); // Approves an value to spend that is lower than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value / 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value / 2); - System::assert_last_event(Event::Approval { id, owner, spender, value: value / 2 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: value / 2 }.into()); // Approves an value to spend that is higher than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value * 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); - System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: value * 2 }.into()); // Approves an value to spend that is equal to the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value * 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); - System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: value * 2 }.into()); // Sets allowance to zero. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, 0)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), 0); - System::assert_last_event(Event::Approval { id, owner, spender, value: 0 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: 0 }.into()); }); } @@ -96,11 +96,11 @@ fn increase_allowance_works() { assert_eq!(0, Assets::allowance(id, &ALICE, &BOB)); assert_ok!(Fungibles::increase_allowance(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); - System::assert_last_event(Event::Approval { id, owner, spender, value }.into()); + System::assert_last_event(Event::Approval { owner, spender, value }.into()); // Additive. assert_ok!(Fungibles::increase_allowance(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); - System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: value * 2 }.into()); }); } @@ -120,11 +120,11 @@ fn decrease_allowance_works() { // Decrease allowance successfully. assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, value / 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value / 2); - System::assert_last_event(Event::Approval { id, owner, spender, value: value / 2 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: value / 2 }.into()); // Saturating if current allowance is decreased more than the owner balance. assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), 0); - System::assert_last_event(Event::Approval { id, owner, spender, value: 0 }.into()); + System::assert_last_event(Event::Approval { owner, spender, value: 0 }.into()); }); } @@ -197,7 +197,7 @@ fn mint_works() { assert_ok!(Fungibles::mint(signed(ALICE), id, BOB, value)); let balance_after_mint = Assets::balance(id, &BOB); assert_eq!(balance_after_mint, balance_before_mint + value); - System::assert_last_event(Event::Transfer { id, from, to, value }.into()); + System::assert_last_event(Event::Transfer { from, to, value }.into()); }); } @@ -214,7 +214,7 @@ fn burn_works() { assert_ok!(Fungibles::burn(signed(ALICE), id, BOB, value)); let balance_after_burn = Assets::balance(id, &BOB); assert_eq!(balance_after_burn, balance_before_burn - value); - System::assert_last_event(Event::Transfer { id, from, to, value }.into()); + System::assert_last_event(Event::Transfer { from, to, value }.into()); }); } From 9ea24888984c6008b38a397103d879b7e5b41b4b Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 12 Aug 2024 18:10:24 +0200 Subject: [PATCH 49/56] refactor: optimization pallet fungibles --- pallets/api/src/fungibles/mod.rs | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index e591ebb0..25415374 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -192,8 +192,6 @@ pub mod pallet { let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); - let spender_unlookup = T::Lookup::unlookup(spender.clone()); - let id_param: AssetIdParameterOf = id.into(); let return_weight = match value.cmp(¤t_allowance) { // If the new value is equal to the current allowance, do nothing. @@ -203,8 +201,8 @@ pub mod pallet { Greater => { AssetsOf::::approve_transfer( origin, - id_param, - spender_unlookup, + id.into(), + T::Lookup::unlookup(spender.clone()), value.saturating_sub(current_allowance), ) .map_err(|e| e.with_weight(Self::weight_approve(1, 0)))?; @@ -213,16 +211,18 @@ pub mod pallet { // If the new value is less than the current allowance, cancel the approval and // set the new value. Less => { + let id: AssetIdParameterOf = id.into(); + let spender_source = T::Lookup::unlookup(spender.clone()); AssetsOf::::cancel_approval( origin.clone(), - id_param.clone(), - spender_unlookup.clone(), + id.clone(), + spender_source.clone(), ) .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; if value.is_zero() { Self::weight_approve(0, 1) } else { - AssetsOf::::approve_transfer(origin, id_param, spender_unlookup, value)?; + AssetsOf::::approve_transfer(origin, id, spender_source, value)?; Self::weight_approve(1, 1) } }, @@ -275,24 +275,20 @@ pub mod pallet { let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); - let spender_unlookup = T::Lookup::unlookup(spender.clone()); - let id_param: AssetIdParameterOf = id.into(); + let spender_source = T::Lookup::unlookup(spender.clone()); + let id: AssetIdParameterOf = id.into(); if value.is_zero() { return Ok(Some(Self::weight_approve(0, 0)).into()); } // Cancel the approval and set the new value if `new_allowance` is more than zero. - AssetsOf::::cancel_approval( - origin.clone(), - id_param.clone(), - spender_unlookup.clone(), - ) - .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; + AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender_source.clone()) + .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; let new_allowance = current_allowance.saturating_sub(value); let weight = if new_allowance.is_zero() { Self::weight_approve(0, 1) } else { - AssetsOf::::approve_transfer(origin, id_param, spender_unlookup, new_allowance)?; + AssetsOf::::approve_transfer(origin, id, spender_source, new_allowance)?; Self::weight_approve(1, 1) }; Self::deposit_event(Event::Approval { owner, spender, value: new_allowance }); From 0904910ddc50f3df7d36c4d1287e421a0b899cba Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 12 Aug 2024 18:32:20 +0200 Subject: [PATCH 50/56] fix: weight increase_allowance --- pallets/api/src/fungibles/mod.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 25415374..448a1792 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -238,14 +238,15 @@ pub mod pallet { /// - `spender` - The account that is allowed to spend the tokens. /// - `value` - The number of tokens to increase the allowance by. #[pallet::call_index(6)] - #[pallet::weight(AssetsWeightInfoOf::::approve_transfer() + T::DbWeight::get().reads(1))] + #[pallet::weight(::WeightInfo::approve(1, 0))] pub fn increase_allowance( origin: OriginFor, id: AssetIdOf, spender: AccountIdOf, value: BalanceOf, ) -> DispatchResultWithPostInfo { - let owner = ensure_signed(origin.clone())?; + let owner = ensure_signed(origin.clone()) + .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; AssetsOf::::approve_transfer( origin, id.clone().into(), From ea2b461e6d2f6820cb63363daa1b0821acd7d2e5 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Mon, 12 Aug 2024 21:17:52 +0200 Subject: [PATCH 51/56] docs: events contracts --- pop-api/src/v0/assets/fungibles.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 6905842d..36fd1702 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -58,6 +58,15 @@ mod constants { pub(super) const BURN: u8 = 20; } +/// A set of events for use in smart contracts interacting with the fungibles API. +/// +/// The `Transfer` and `Approval` events conform to the PSP-22 standard. The other events +/// (`Create`, `StartDestroy`, `SetMetadata`, `ClearMetadata`) are provided for convenience. +/// +/// These events are not emitted by the API itself but can be used in your contracts to +/// track asset operations. Be mindful of the costs associated with emitting events. +/// +/// For more details, refer to [ink! events](https://use.ink/basics/events). pub mod events { use super::*; From 5093e88099d539156e11c3a9860698f7b1247284 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 13 Aug 2024 10:11:47 +0200 Subject: [PATCH 52/56] refactor: remove unnecessary event topic --- pop-api/src/v0/assets/fungibles.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pop-api/src/v0/assets/fungibles.rs b/pop-api/src/v0/assets/fungibles.rs index 36fd1702..323db2b6 100644 --- a/pop-api/src/v0/assets/fungibles.rs +++ b/pop-api/src/v0/assets/fungibles.rs @@ -124,14 +124,13 @@ pub mod events { /// The ID of the asset created. #[ink(topic)] pub id: AssetId, - #[ink(topic)] /// The name of the asset. + #[ink(topic)] pub name: Vec, /// The symbol of the asset. #[ink(topic)] pub symbol: Vec, /// The decimals of the asset. - #[ink(topic)] pub decimals: u8, } From 3848faa721c9c92e0e8ff16c9c39436c0440a504 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Tue, 13 Aug 2024 20:14:08 +0200 Subject: [PATCH 53/56] fix: add asset ID to event --- pallets/api/src/fungibles/mod.rs | 58 +++++++++++++++++++----------- pallets/api/src/fungibles/tests.rs | 26 +++++++------- 2 files changed, 51 insertions(+), 33 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 448a1792..3642b95f 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -101,6 +101,8 @@ pub mod pallet { pub enum Event { /// Event emitted when allowance by `owner` to `spender` changes. Approval { + /// The ID of the asset. + id: AssetIdOf, /// Account providing allowance. owner: AccountIdOf, /// Allowance beneficiary. @@ -110,6 +112,8 @@ pub mod pallet { }, /// Event emitted when transfer of tokens occurs. Transfer { + /// The ID of the asset. + id: AssetIdOf, /// Transfer sender. `None` in case of minting new tokens. from: Option>, /// Transfer recipient. `None` in case of burning tokens. @@ -138,12 +142,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::transfer_keep_alive( origin.clone(), - id.into(), + id.clone().into(), T::Lookup::unlookup(to.clone()), value, )?; let from = ensure_signed(origin)?; - Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Self::deposit_event(Event::Transfer { id, from: Some(from), to: Some(to), value }); Ok(()) } @@ -166,12 +170,12 @@ pub mod pallet { ) -> DispatchResult { AssetsOf::::transfer_approved( origin, - id.into(), + id.clone().into(), T::Lookup::unlookup(from.clone()), T::Lookup::unlookup(to.clone()), value, )?; - Self::deposit_event(Event::Transfer { from: Some(from), to: Some(to), value }); + Self::deposit_event(Event::Transfer { id, from: Some(from), to: Some(to), value }); Ok(()) } @@ -201,7 +205,7 @@ pub mod pallet { Greater => { AssetsOf::::approve_transfer( origin, - id.into(), + id.clone().into(), T::Lookup::unlookup(spender.clone()), value.saturating_sub(current_allowance), ) @@ -211,23 +215,23 @@ pub mod pallet { // If the new value is less than the current allowance, cancel the approval and // set the new value. Less => { - let id: AssetIdParameterOf = id.into(); + let id_param: AssetIdParameterOf = id.clone().into(); let spender_source = T::Lookup::unlookup(spender.clone()); AssetsOf::::cancel_approval( origin.clone(), - id.clone(), + id_param.clone(), spender_source.clone(), ) .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; if value.is_zero() { Self::weight_approve(0, 1) } else { - AssetsOf::::approve_transfer(origin, id, spender_source, value)?; + AssetsOf::::approve_transfer(origin, id_param, spender_source, value)?; Self::weight_approve(1, 1) } }, }; - Self::deposit_event(Event::Approval { owner, spender, value }); + Self::deposit_event(Event::Approval { id, owner, spender, value }); Ok(Some(return_weight).into()) } @@ -254,8 +258,8 @@ pub mod pallet { value, ) .map_err(|e| e.with_weight(AssetsWeightInfoOf::::approve_transfer()))?; - let value = AssetsOf::::allowance(id, &owner, &spender); - Self::deposit_event(Event::Approval { owner, spender, value }); + let value = AssetsOf::::allowance(id.clone(), &owner, &spender); + Self::deposit_event(Event::Approval { id, owner, spender, value }); Ok(().into()) } @@ -277,22 +281,26 @@ pub mod pallet { .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); let spender_source = T::Lookup::unlookup(spender.clone()); - let id: AssetIdParameterOf = id.into(); + let id_param: AssetIdParameterOf = id.clone().into(); if value.is_zero() { return Ok(Some(Self::weight_approve(0, 0)).into()); } // Cancel the approval and set the new value if `new_allowance` is more than zero. - AssetsOf::::cancel_approval(origin.clone(), id.clone(), spender_source.clone()) - .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; + AssetsOf::::cancel_approval( + origin.clone(), + id_param.clone(), + spender_source.clone(), + ) + .map_err(|e| e.with_weight(Self::weight_approve(0, 1)))?; let new_allowance = current_allowance.saturating_sub(value); let weight = if new_allowance.is_zero() { Self::weight_approve(0, 1) } else { - AssetsOf::::approve_transfer(origin, id, spender_source, new_allowance)?; + AssetsOf::::approve_transfer(origin, id_param, spender_source, new_allowance)?; Self::weight_approve(1, 1) }; - Self::deposit_event(Event::Approval { owner, spender, value: new_allowance }); + Self::deposit_event(Event::Approval { id, owner, spender, value: new_allowance }); Ok(Some(weight).into()) } @@ -368,8 +376,13 @@ pub mod pallet { account: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - AssetsOf::::mint(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; - Self::deposit_event(Event::Transfer { from: None, to: Some(account), value }); + AssetsOf::::mint( + origin, + id.clone().into(), + T::Lookup::unlookup(account.clone()), + value, + )?; + Self::deposit_event(Event::Transfer { id, from: None, to: Some(account), value }); Ok(()) } @@ -387,8 +400,13 @@ pub mod pallet { account: AccountIdOf, value: BalanceOf, ) -> DispatchResult { - AssetsOf::::burn(origin, id.into(), T::Lookup::unlookup(account.clone()), value)?; - Self::deposit_event(Event::Transfer { from: Some(account), to: None, value }); + AssetsOf::::burn( + origin, + id.clone().into(), + T::Lookup::unlookup(account.clone()), + value, + )?; + Self::deposit_event(Event::Transfer { id, from: Some(account), to: None, value }); Ok(()) } } diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index a30aaa2f..a77d7e60 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -24,7 +24,7 @@ fn transfer_works() { assert_ok!(Fungibles::transfer(signed(ALICE), id, BOB, value)); let balance_after_transfer = Assets::balance(id, &BOB); assert_eq!(balance_after_transfer, balance_before_transfer + value); - System::assert_last_event(Event::Transfer { from, to, value }.into()); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } @@ -47,7 +47,7 @@ fn transfer_from_works() { // Check that BOB receives the `value` and ALICE `amount` is spent successfully by CHARLIE. assert_eq!(bob_balance_after_transfer, bob_balance_before_transfer + value); assert_eq!(alice_balance_after_transfer, alice_balance_before_transfer - value); - System::assert_last_event(Event::Transfer { from, to, value }.into()); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } @@ -64,23 +64,23 @@ fn approve_works() { assert_eq!(0, Assets::allowance(id, &ALICE, &BOB)); assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); - System::assert_last_event(Event::Approval { owner, spender, value }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value }.into()); // Approves an value to spend that is lower than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value / 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value / 2); - System::assert_last_event(Event::Approval { owner, spender, value: value / 2 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: value / 2 }.into()); // Approves an value to spend that is higher than the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value * 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); - System::assert_last_event(Event::Approval { owner, spender, value: value * 2 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); // Approves an value to spend that is equal to the current allowance. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, value * 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); - System::assert_last_event(Event::Approval { owner, spender, value: value * 2 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); // Sets allowance to zero. assert_ok!(Fungibles::approve(signed(ALICE), id, BOB, 0)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), 0); - System::assert_last_event(Event::Approval { owner, spender, value: 0 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: 0 }.into()); }); } @@ -96,11 +96,11 @@ fn increase_allowance_works() { assert_eq!(0, Assets::allowance(id, &ALICE, &BOB)); assert_ok!(Fungibles::increase_allowance(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value); - System::assert_last_event(Event::Approval { owner, spender, value }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value }.into()); // Additive. assert_ok!(Fungibles::increase_allowance(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value * 2); - System::assert_last_event(Event::Approval { owner, spender, value: value * 2 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: value * 2 }.into()); }); } @@ -120,11 +120,11 @@ fn decrease_allowance_works() { // Decrease allowance successfully. assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, value / 2)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), value / 2); - System::assert_last_event(Event::Approval { owner, spender, value: value / 2 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: value / 2 }.into()); // Saturating if current allowance is decreased more than the owner balance. assert_ok!(Fungibles::decrease_allowance(signed(ALICE), id, BOB, value)); assert_eq!(Assets::allowance(id, &ALICE, &BOB), 0); - System::assert_last_event(Event::Approval { owner, spender, value: 0 }.into()); + System::assert_last_event(Event::Approval { id, owner, spender, value: 0 }.into()); }); } @@ -197,7 +197,7 @@ fn mint_works() { assert_ok!(Fungibles::mint(signed(ALICE), id, BOB, value)); let balance_after_mint = Assets::balance(id, &BOB); assert_eq!(balance_after_mint, balance_before_mint + value); - System::assert_last_event(Event::Transfer { from, to, value }.into()); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } @@ -214,7 +214,7 @@ fn burn_works() { assert_ok!(Fungibles::burn(signed(ALICE), id, BOB, value)); let balance_after_burn = Assets::balance(id, &BOB); assert_eq!(balance_after_burn, balance_before_burn - value); - System::assert_last_event(Event::Transfer { from, to, value }.into()); + System::assert_last_event(Event::Transfer { id, from, to, value }.into()); }); } From 6a9cd00618f7543a175b8c10658895163407d11a Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 14 Aug 2024 09:08:05 +0200 Subject: [PATCH 54/56] feat: add Create event --- pallets/api/src/fungibles/mod.rs | 19 ++++++++++++++++++- pallets/api/src/fungibles/tests.rs | 5 ++++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 3642b95f..4e7b4df7 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -121,6 +121,15 @@ pub mod pallet { /// Amount of tokens transferred (or minted/burned). value: BalanceOf, }, + /// Event emitted when a token class is created. + Create { + /// The ID of the asset. + id: AssetIdOf, + /// Creator of the asset. + creator: AccountIdOf, + /// Admin of the asset. + admin: AccountIdOf, + }, } #[pallet::call] @@ -318,7 +327,15 @@ pub mod pallet { admin: AccountIdOf, min_balance: BalanceOf, ) -> DispatchResult { - AssetsOf::::create(origin, id.into(), T::Lookup::unlookup(admin), min_balance) + AssetsOf::::create( + origin.clone(), + id.clone().into(), + T::Lookup::unlookup(admin.clone()), + min_balance, + )?; + let creator = ensure_signed(origin)?; + Self::deposit_event(Event::Create { id, creator, admin }); + Ok(()) } /// Start the process of destroying a token with a given asset ID. diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index a77d7e60..044f0ed4 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -132,10 +132,13 @@ fn decrease_allowance_works() { fn create_works() { new_test_ext().execute_with(|| { let id = ASSET; + let creator = ALICE; + let admin = ALICE; assert!(!Assets::asset_exists(id)); - assert_ok!(Fungibles::create(signed(ALICE), id, ALICE, 100)); + assert_ok!(Fungibles::create(signed(creator), id, admin, 100)); assert!(Assets::asset_exists(id)); + System::assert_last_event(Event::Create { id, creator, admin }.into()); }); } From 3b3ae9fcf4de6115bc5a2ecb9a92a2ca2e0d0068 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 14 Aug 2024 09:17:10 +0200 Subject: [PATCH 55/56] refactor: fungible pallet --- pallets/api/src/fungibles/mod.rs | 10 +++++----- pallets/api/src/fungibles/tests.rs | 7 ++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 4e7b4df7..8219e3c8 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -206,7 +206,7 @@ pub mod pallet { .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); - let return_weight = match value.cmp(¤t_allowance) { + let weight = match value.cmp(¤t_allowance) { // If the new value is equal to the current allowance, do nothing. Equal => Self::weight_approve(0, 0), // If the new value is greater than the current allowance, approve the difference @@ -241,7 +241,7 @@ pub mod pallet { }, }; Self::deposit_event(Event::Approval { id, owner, spender, value }); - Ok(Some(return_weight).into()) + Ok(Some(weight).into()) } /// Increases the allowance of a spender. @@ -288,13 +288,13 @@ pub mod pallet { ) -> DispatchResultWithPostInfo { let owner = ensure_signed(origin.clone()) .map_err(|e| e.with_weight(Self::weight_approve(0, 0)))?; + if value.is_zero() { + return Ok(Some(Self::weight_approve(0, 0)).into()); + } let current_allowance = AssetsOf::::allowance(id.clone(), &owner, &spender); let spender_source = T::Lookup::unlookup(spender.clone()); let id_param: AssetIdParameterOf = id.clone().into(); - if value.is_zero() { - return Ok(Some(Self::weight_approve(0, 0)).into()); - } // Cancel the approval and set the new value if `new_allowance` is more than zero. AssetsOf::::cancel_approval( origin.clone(), diff --git a/pallets/api/src/fungibles/tests.rs b/pallets/api/src/fungibles/tests.rs index 044f0ed4..ca2a85c9 100644 --- a/pallets/api/src/fungibles/tests.rs +++ b/pallets/api/src/fungibles/tests.rs @@ -2,6 +2,7 @@ use crate::{fungibles::Read::*, mock::*}; use codec::Encode; use frame_support::{ assert_ok, + sp_runtime::traits::Zero, traits::fungibles::{ approvals::Inspect as ApprovalInspect, metadata::Inspect as MetadataInspect, Inspect, }, @@ -181,9 +182,9 @@ fn clear_metadata_works() { create_asset_and_set_metadata(ALICE, id, vec![42], vec![42], 42); assert_ok!(Fungibles::clear_metadata(signed(ALICE), id)); - assert_eq!(Assets::name(id), Vec::::new()); - assert_eq!(Assets::symbol(id), Vec::::new()); - assert_eq!(Assets::decimals(id), 0u8); + assert!(Assets::name(id).is_empty()); + assert!(Assets::symbol(id).is_empty()); + assert!(Assets::decimals(id).is_zero()); }); } From fa7d258d298b072bdd72ffa7a31cb43facbc8a64 Mon Sep 17 00:00:00 2001 From: Daanvdplas Date: Wed, 14 Aug 2024 09:44:59 +0200 Subject: [PATCH 56/56] refactor: consistency --- pallets/api/src/fungibles/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pallets/api/src/fungibles/mod.rs b/pallets/api/src/fungibles/mod.rs index 8219e3c8..7aa9ac00 100644 --- a/pallets/api/src/fungibles/mod.rs +++ b/pallets/api/src/fungibles/mod.rs @@ -327,13 +327,13 @@ pub mod pallet { admin: AccountIdOf, min_balance: BalanceOf, ) -> DispatchResult { + let creator = ensure_signed(origin.clone())?; AssetsOf::::create( - origin.clone(), + origin, id.clone().into(), T::Lookup::unlookup(admin.clone()), min_balance, )?; - let creator = ensure_signed(origin)?; Self::deposit_event(Event::Create { id, creator, admin }); Ok(()) }