From 8e76398fa1f070a1361a77b04269c2c59c08d76a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Dino=20Pa=C4=8Dandi?= <3002868+Dinonard@users.noreply.github.com> Date: Wed, 21 Dec 2022 14:03:49 +0100 Subject: [PATCH] Add CallbackHandle to pallet-assets (#12307) * Add CallbackHandle to pallet-assets * Address review comments * Add use for sp_io::storage * Rebase & review comments * Fix UT Co-authored-by: Hoon Kim --- bin/node/runtime/src/lib.rs | 1 + frame/assets/src/functions.rs | 4 ++- frame/assets/src/lib.rs | 29 +++++++++++++++++- frame/assets/src/mock.rs | 19 +++++++++++- frame/assets/src/tests.rs | 30 +++++++++++++++++++ .../asset-tx-payment/src/tests.rs | 1 + 6 files changed, 81 insertions(+), 3 deletions(-) diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 0e3bee8821fc2..e5776e3fd692c 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1478,6 +1478,7 @@ impl pallet_assets::Config for Runtime { type StringLimit = StringLimit; type Freezer = (); type Extra = (); + type CallbackHandle = (); type WeightInfo = pallet_assets::weights::SubstrateWeight; type RemoveItemsLimit = ConstU32<1000>; #[cfg(feature = "runtime-benchmarks")] diff --git a/frame/assets/src/functions.rs b/frame/assets/src/functions.rs index f7f11cafecbe2..2cdf2d7c4c8f6 100644 --- a/frame/assets/src/functions.rs +++ b/frame/assets/src/functions.rs @@ -664,7 +664,8 @@ impl, I: 'static> Pallet { status: AssetStatus::Live, }, ); - Self::deposit_event(Event::ForceCreated { asset_id: id, owner }); + Self::deposit_event(Event::ForceCreated { asset_id: id, owner: owner.clone() }); + T::CallbackHandle::created(&id, &owner); Ok(()) } @@ -754,6 +755,7 @@ impl, I: 'static> Pallet { approvals_destroyed: removed_approvals as u32, approvals_remaining: details.approvals as u32, }); + T::CallbackHandle::destroyed(&id); Ok(()) })?; Ok(removed_approvals) diff --git a/frame/assets/src/lib.rs b/frame/assets/src/lib.rs index ab589c0eef0f4..1853ef75f52be 100644 --- a/frame/assets/src/lib.rs +++ b/frame/assets/src/lib.rs @@ -116,6 +116,11 @@ //! //! Please refer to the [`Pallet`] struct for details on publicly available functions. //! +//! ### Callbacks +//! +//! Using `CallbackHandle` associated type, user can configure custom callback functions which are +//! executed when new asset is created or an existing asset is destroyed. +//! //! ## Related Modules //! //! * [`System`](../frame_system/index.html) @@ -171,6 +176,18 @@ pub use weights::WeightInfo; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; +/// Trait with callbacks that are executed after successfull asset creation or destruction. +pub trait AssetsCallback { + /// Indicates that asset with `id` was successfully created by the `owner` + fn created(_id: &AssetId, _owner: &AccountId) {} + + /// Indicates that asset with `id` has just been destroyed + fn destroyed(_id: &AssetId) {} +} + +/// Empty implementation in case no callbacks are required. +impl AssetsCallback for () {} + #[frame_support::pallet] pub mod pallet { use super::*; @@ -283,6 +300,9 @@ pub mod pallet { /// Additional data to be stored with an account's asset balance. type Extra: Member + Parameter + Default + MaxEncodedLen; + /// Callback methods for asset state change (e.g. asset created or destroyed) + type CallbackHandle: AssetsCallback; + /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; @@ -598,7 +618,14 @@ pub mod pallet { status: AssetStatus::Live, }, ); - Self::deposit_event(Event::Created { asset_id: id, creator: owner, owner: admin }); + + Self::deposit_event(Event::Created { + asset_id: id, + creator: owner.clone(), + owner: admin, + }); + T::CallbackHandle::created(&id, &owner); + Ok(()) } diff --git a/frame/assets/src/mock.rs b/frame/assets/src/mock.rs index 06b6ccf06c57e..93a87ee6714c6 100644 --- a/frame/assets/src/mock.rs +++ b/frame/assets/src/mock.rs @@ -20,11 +20,13 @@ use super::*; use crate as pallet_assets; +use codec::Encode; use frame_support::{ construct_runtime, parameter_types, traits::{AsEnsureOriginWithArg, ConstU32, ConstU64, GenesisBuild}, }; use sp_core::H256; +use sp_io::storage; use sp_runtime::{ testing::Header, traits::{BlakeTwo256, IdentityLookup}, @@ -45,6 +47,9 @@ construct_runtime!( } ); +type AccountId = u64; +type AssetId = u32; + impl frame_system::Config for Test { type BaseCallFilter = frame_support::traits::Everything; type BlockWeights = (); @@ -55,7 +60,7 @@ impl frame_system::Config for Test { type BlockNumber = u64; type Hash = H256; type Hashing = BlakeTwo256; - type AccountId = u64; + type AccountId = AccountId; type Lookup = IdentityLookup; type Header = Header; type RuntimeEvent = RuntimeEvent; @@ -84,6 +89,17 @@ impl pallet_balances::Config for Test { type ReserveIdentifier = [u8; 8]; } +pub struct AssetsCallbackHandle; +impl AssetsCallback for AssetsCallbackHandle { + fn created(_id: &AssetId, _owner: &AccountId) { + storage::set(b"asset_created", &().encode()); + } + + fn destroyed(_id: &AssetId) { + storage::set(b"asset_destroyed", &().encode()); + } +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type Balance = u64; @@ -100,6 +116,7 @@ impl Config for Test { type StringLimit = ConstU32<50>; type Freezer = TestFreezer; type WeightInfo = (); + type CallbackHandle = AssetsCallbackHandle; type Extra = (); type RemoveItemsLimit = ConstU32<5>; #[cfg(feature = "runtime-benchmarks")] diff --git a/frame/assets/src/tests.rs b/frame/assets/src/tests.rs index d5fcece0e91d8..f7007ff3ccf17 100644 --- a/frame/assets/src/tests.rs +++ b/frame/assets/src/tests.rs @@ -24,6 +24,7 @@ use frame_support::{ traits::{fungibles::InspectEnumerable, Currency}, }; use pallet_balances::Error as BalancesError; +use sp_io::storage; use sp_runtime::{traits::ConvertInto, TokenError}; fn asset_ids() -> Vec { @@ -1194,3 +1195,32 @@ fn querying_roles_should_work() { assert_eq!(Assets::freezer(0), Some(4)); }); } + +#[test] +fn normal_asset_create_and_destroy_callbacks_should_work() { + new_test_ext().execute_with(|| { + assert!(storage::get(b"asset_created").is_none()); + assert!(storage::get(b"asset_destroyed").is_none()); + + Balances::make_free_balance_be(&1, 100); + assert_ok!(Assets::create(RuntimeOrigin::signed(1), 0, 1, 1)); + assert!(storage::get(b"asset_created").is_some()); + assert!(storage::get(b"asset_destroyed").is_none()); + + assert_ok!(Assets::start_destroy(RuntimeOrigin::signed(1), 0)); + assert_ok!(Assets::destroy_accounts(RuntimeOrigin::signed(1), 0)); + assert_ok!(Assets::destroy_approvals(RuntimeOrigin::signed(1), 0)); + assert_ok!(Assets::finish_destroy(RuntimeOrigin::signed(1), 0)); + assert!(storage::get(b"asset_destroyed").is_some()); + }); +} + +#[test] +fn root_asset_create_should_work() { + new_test_ext().execute_with(|| { + assert!(storage::get(b"asset_created").is_none()); + assert_ok!(Assets::force_create(RuntimeOrigin::root(), 0, 1, true, 1)); + assert!(storage::get(b"asset_created").is_some()); + assert!(storage::get(b"asset_destroyed").is_none()); + }); +} diff --git a/frame/transaction-payment/asset-tx-payment/src/tests.rs b/frame/transaction-payment/asset-tx-payment/src/tests.rs index b70a88d02c6e1..b79b87a93c7a4 100644 --- a/frame/transaction-payment/asset-tx-payment/src/tests.rs +++ b/frame/transaction-payment/asset-tx-payment/src/tests.rs @@ -171,6 +171,7 @@ impl pallet_assets::Config for Runtime { type StringLimit = ConstU32<20>; type Freezer = (); type Extra = (); + type CallbackHandle = (); type WeightInfo = (); type RemoveItemsLimit = ConstU32<1000>; pallet_assets::runtime_benchmarks_enabled! {