diff --git a/pallets/foreign-asset-creator/src/lib.rs b/pallets/foreign-asset-creator/src/lib.rs index 0c702a89..af6e89d3 100644 --- a/pallets/foreign-asset-creator/src/lib.rs +++ b/pallets/foreign-asset-creator/src/lib.rs @@ -27,6 +27,35 @@ pub mod mock; #[cfg(test)] pub mod tests; +/// Trait for the OnForeignAssetRegistered hook +pub trait ForeignAssetCreatedHook { + fn on_asset_created( + foreign_asset: &ForeignAsset, + asset_id: &AssetId, + min_balance: &AssetBalance, + ); +} + +impl + ForeignAssetCreatedHook for () +{ + fn on_asset_created( + _foreign_asset: &ForeignAsset, + _asset_id: &AssetId, + _min_balance: &AssetBalance, + ) { + } +} + +/// Trait for the OnForeignAssetDeregistered hook +pub trait ForeignAssetDestroyedHook { + fn on_asset_destroyed(foreign_asset: &ForeignAsset, asset_id: &AssetId); +} + +impl ForeignAssetDestroyedHook for () { + fn on_asset_destroyed(_foreign_asset: &ForeignAsset, _asset_id: &AssetId) {} +} + #[pallet] pub mod pallet { use super::*; @@ -66,6 +95,16 @@ pub mod pallet { /// Weight information for extrinsics in this pallet. type WeightInfo: WeightInfo; + + /// Hook to be called when new foreign asset is registered. + type OnForeignAssetCreated: ForeignAssetCreatedHook< + Self::ForeignAsset, + AssetId, + AssetBalance, + >; + + /// Hook to be called when foreign asset is de-registered. + type OnForeignAssetDestroyed: ForeignAssetDestroyedHook>; } pub type AssetBalance = <::Fungibles as fungibles::Inspect< @@ -152,6 +191,8 @@ pub mod pallet { AssetIdToForeignAsset::::insert(&asset_id, &foreign_asset); ForeignAssetToAssetId::::insert(&foreign_asset, &asset_id); + T::OnForeignAssetCreated::on_asset_created(&foreign_asset, &asset_id, &min_balance); + Self::deposit_event(Event::ForeignAssetCreated { asset_id, foreign_asset, @@ -233,6 +274,8 @@ pub mod pallet { // Remove from ForeignAssetToAssetId ForeignAssetToAssetId::::remove(&foreign_asset); + T::OnForeignAssetDestroyed::on_asset_destroyed(&foreign_asset, &asset_id); + Self::deposit_event(Event::ForeignAssetDestroyed { asset_id, foreign_asset, diff --git a/pallets/foreign-asset-creator/src/mock.rs b/pallets/foreign-asset-creator/src/mock.rs index cdc83c26..4338d7da 100644 --- a/pallets/foreign-asset-creator/src/mock.rs +++ b/pallets/foreign-asset-creator/src/mock.rs @@ -16,12 +16,14 @@ use super::*; use crate as pallet_foreign_asset_creator; +use std::marker::PhantomData; use frame_support::{ - construct_runtime, parameter_types, + construct_runtime, parameter_types, storage, traits::{ConstU32, Everything}, }; use frame_system::EnsureRoot; +use parity_scale_codec::{Decode, Encode}; use sp_core::H256; use sp_runtime::traits::{BlakeTwo256, IdentityLookup}; use sp_runtime::BuildStorage; @@ -126,6 +128,77 @@ impl pallet_assets::Config for Test { } } +/// Gets parameters of last `ForeignAssetCreatedHook::on_asset_created` hook invocation +pub fn get_asset_created_hook_invocation< + ForeignAsset: Decode, + AssetId: Decode, + AssetBalance: Decode, +>() -> Option<(ForeignAsset, AssetId, AssetBalance)> { + storage::unhashed::get_raw(b"____on_foreign_asset_created") + .map(|output| Decode::decode(&mut output.as_slice()).expect("Decoding should work")) +} + +/// Notes down parameters of current `ForeignAssetCreatedHook::on_asset_created` hook invocation +fn note_on_asset_created_hook_invocation< + ForeignAsset: Encode, + AssetId: Encode, + AssetBalance: Encode, +>( + foreign_asset: &ForeignAsset, + asset_id: &AssetId, + min_balance: &AssetBalance, +) { + storage::unhashed::put_raw( + b"____on_foreign_asset_created", + (foreign_asset, asset_id, min_balance).encode().as_slice(), + ); +} + +/// Gets parameters of last `ForeignAssetDestroyedHook::on_asset_destroyed` hook invocation +pub fn get_asset_destroyed_hook_invocation( +) -> Option<(ForeignAsset, AssetId)> { + storage::unhashed::get_raw(b"____on_foreign_asset_destroyed") + .map(|output| Decode::decode(&mut output.as_slice()).expect("Decoding should work")) +} + +/// Notes down parameters of current `ForeignAssetDestroyedHook::on_asset_destroyed` hook invocation +fn note_on_asset_destroyed_hook_invocation( + foreign_asset: &ForeignAsset, + asset_id: &AssetId, +) { + storage::unhashed::put_raw( + b"____on_foreign_asset_destroyed", + (foreign_asset, asset_id).encode().as_slice(), + ); +} + +/// Test hook that records the hook invocation with exact params +pub struct NoteDownHook( + PhantomData<(ForeignAsset, AssetId, AssetBalance)>, +); + +impl + ForeignAssetCreatedHook + for NoteDownHook +{ + fn on_asset_created( + foreign_asset: &ForeignAsset, + asset_id: &AssetId, + min_balance: &AssetBalance, + ) { + note_on_asset_created_hook_invocation(foreign_asset, asset_id, min_balance); + } +} + +impl + ForeignAssetDestroyedHook + for NoteDownHook +{ + fn on_asset_destroyed(foreign_asset: &ForeignAsset, asset_id: &AssetId) { + note_on_asset_destroyed_hook_invocation(foreign_asset, asset_id); + } +} + impl Config for Test { type RuntimeEvent = RuntimeEvent; type ForeignAsset = Location; @@ -134,6 +207,8 @@ impl Config for Test { type ForeignAssetDestroyerOrigin = EnsureRoot; type Fungibles = Assets; type WeightInfo = (); + type OnForeignAssetCreated = NoteDownHook; + type OnForeignAssetDestroyed = NoteDownHook; } pub(crate) struct ExtBuilder { diff --git a/pallets/foreign-asset-creator/src/tests.rs b/pallets/foreign-asset-creator/src/tests.rs index fca9c765..44b31e49 100644 --- a/pallets/foreign-asset-creator/src/tests.rs +++ b/pallets/foreign-asset-creator/src/tests.rs @@ -42,7 +42,14 @@ fn creating_foreign_works() { expect_events(vec![crate::Event::ForeignAssetCreated { asset_id: 1, foreign_asset: Location::parent(), - }]) + }]); + + let (foreign_asset, asset_id, min_balance): (Location, u32, u64) = + get_asset_created_hook_invocation() + .expect("Decoding of invocation data should not fail"); + assert_eq!(foreign_asset, Location::parent()); + assert_eq!(asset_id, 1u32); + assert_eq!(min_balance, 1u64); }); } @@ -205,6 +212,13 @@ fn test_destroy_foreign_asset_also_removes_everything() { 1u64, )); + let (foreign_asset, asset_id, min_balance): (Location, u32, u64) = + get_asset_created_hook_invocation() + .expect("Decoding of invocation data should not fail"); + assert_eq!(foreign_asset, Location::parent()); + assert_eq!(asset_id, 1u32); + assert_eq!(min_balance, 1u64); + assert_ok!(ForeignAssetCreator::destroy_foreign_asset( RuntimeOrigin::root(), 1 @@ -223,6 +237,11 @@ fn test_destroy_foreign_asset_also_removes_everything() { asset_id: 1, foreign_asset: Location::parent(), }, - ]) + ]); + + let (foreign_asset, asset_id): (Location, u32) = get_asset_destroyed_hook_invocation() + .expect("Decoding of invocation data should not fail"); + assert_eq!(foreign_asset, Location::parent()); + assert_eq!(asset_id, 1u32); }); }