diff --git a/Cargo.lock b/Cargo.lock index ff2f483f52..b23dd5fd9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -271,6 +271,7 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-restricted-tokens", + "pallet-restricted-xtokens", "pallet-rewards", "pallet-scheduler", "pallet-session", @@ -1219,6 +1220,7 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-restricted-tokens", + "pallet-restricted-xtokens", "pallet-rewards", "pallet-scheduler", "pallet-session", @@ -2825,6 +2827,7 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-restricted-tokens", + "pallet-restricted-xtokens", "pallet-rewards", "pallet-scheduler", "pallet-session", @@ -8466,6 +8469,22 @@ dependencies = [ "sp-std", ] +[[package]] +name = "pallet-restricted-xtokens" +version = "0.0.1" +dependencies = [ + "cfg-traits", + "frame-support", + "frame-system", + "orml-traits", + "orml-xtokens", + "parity-scale-codec 3.6.5", + "scale-info", + "serde", + "sp-std", + "xcm", +] + [[package]] name = "pallet-rewards" version = "0.1.0" @@ -8740,7 +8759,6 @@ dependencies = [ name = "pallet-transfer-allowlist" version = "0.1.0" dependencies = [ - "cfg-mocks", "cfg-traits", "cfg-types", "frame-benchmarking", @@ -11454,6 +11472,7 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-restricted-tokens", + "pallet-restricted-xtokens", "pallet-rewards", "pallet-scheduler", "pallet-session", @@ -11514,6 +11533,7 @@ dependencies = [ "fudge-core", "getrandom 0.2.10", "hex", + "hex-literal 0.3.4", "kusama-runtime", "lazy_static", "liquidity-pools-gateway-routers", @@ -11569,6 +11589,7 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-restricted-tokens", + "pallet-restricted-xtokens", "pallet-rewards", "pallet-scheduler", "pallet-session", diff --git a/Cargo.toml b/Cargo.toml index 2dfc799567..680e88034c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -62,6 +62,7 @@ members = [ "pallets/pool-registry", "pallets/data-collector", "pallets/restricted-tokens", + "pallets/restricted-xtokens", "pallets/transfer-allowlist", "pallets/rewards", "runtime/altair", @@ -248,6 +249,7 @@ pallet-permissions = { path = "pallets/permissions", default-features = false } pallet-pool-registry = { path = "pallets/pool-registry", default-features = false } pallet-pool-system = { path = "pallets/pool-system", default-features = false } pallet-restricted-tokens = { path = "pallets/restricted-tokens", default-features = false } +pallet-restricted-xtokens = { path = "pallets/restricted-xtokens", default-features = false } pallet-rewards = { path = "pallets/rewards", default-features = false } pallet-transfer-allowlist = { path = "pallets/transfer-allowlist", default-features = false } diff --git a/libs/types/Cargo.toml b/libs/types/Cargo.toml index f96a4a7ead..5c2e86eeb0 100644 --- a/libs/types/Cargo.toml +++ b/libs/types/Cargo.toml @@ -32,6 +32,7 @@ cfg-primitives = { path = "../primitives", default-features = false } cfg-traits = { path = "../traits", default-features = false } cfg-utils = { path = "../utils", default-features = false } + [dev-dependencies] frame-support = { git = "https://github.com/paritytech/substrate", default-features = true, branch = "polkadot-v0.9.43" } hex = { version = "0.4.3", default_features = false } diff --git a/libs/types/src/locations.rs b/libs/types/src/locations.rs index 3e8f4f719b..62f0a6c2fd 100644 --- a/libs/types/src/locations.rs +++ b/libs/types/src/locations.rs @@ -18,7 +18,7 @@ use sp_runtime::{ traits::{BlakeTwo256, Hash}, AccountId32, }; -use xcm::{latest::MultiLocation, VersionedMultiLocation}; +use xcm::{v3::MultiLocation, VersionedMultiLocation}; use crate::domain_address::DomainAddress; /// Location types for destinations that can receive restricted transfers diff --git a/pallets/liquidity-pools/src/lib.rs b/pallets/liquidity-pools/src/lib.rs index 5961b87e20..962233a1f3 100644 --- a/pallets/liquidity-pools/src/lib.rs +++ b/pallets/liquidity-pools/src/lib.rs @@ -116,7 +116,8 @@ pub type GeneralCurrencyIndexOf = pub mod pallet { use cfg_traits::{ investments::{ForeignInvestment, TrancheCurrency}, - CurrencyInspect, Permissions, PoolInspect, Seconds, TimeAsSecs, TrancheTokenPrice, + CurrencyInspect, Permissions, PoolInspect, PreConditions, Seconds, TimeAsSecs, + TrancheTokenPrice, }; use cfg_types::{ permissions::{PermissionScope, PoolRole, Role}, @@ -278,6 +279,11 @@ pub mod pallet { /// message. type TreasuryAccount: Get; + type PreTransferFilter: PreConditions< + (Self::AccountId, DomainAddress, Self::CurrencyId), + Result = DispatchResult, + >; + type RuntimeEvent: From> + IsType<::RuntimeEvent>; } @@ -565,6 +571,12 @@ pub mod pallet { // Ensure pool and tranche exist and derive invest id let invest_id = Self::derive_invest_id(pool_id, tranche_id)?; + T::PreTransferFilter::check(( + who.clone(), + domain_address.clone(), + invest_id.clone().into(), + ))?; + // Transfer to the domain account for bookkeeping T::Tokens::transfer( invest_id.into(), @@ -627,6 +639,8 @@ pub mod pallet { } } + T::PreTransferFilter::check((who.clone(), receiver.clone(), currency_id))?; + // Transfer to the domain account for bookkeeping T::Tokens::transfer( currency_id, diff --git a/pallets/restricted-xtokens/Cargo.toml b/pallets/restricted-xtokens/Cargo.toml new file mode 100644 index 0000000000..d1389cf186 --- /dev/null +++ b/pallets/restricted-xtokens/Cargo.toml @@ -0,0 +1,56 @@ +[package] +name = "pallet-restricted-xtokens" +authors.workspace = true +description = "A wrapper pallet around orml-xtokens to allow restricting transfers" +edition.workspace = true +license.workspace = true +repository.workspace = true +version = "0.0.1" + +[dependencies] +codec = { package = "parity-scale-codec", workspace = true } +scale-info = { workspace = true } +serde = { workspace = true, optional = true } + +# substrate +frame-support = { workspace = true } +frame-system = { workspace = true } +sp-std = { workspace = true } + +# orml +orml-xtokens = { workspace = true } +orml-traits = { workspace = true } + +# polkadot +xcm = { workspace = true } + +# local +cfg-traits = { workspace = true } + +[features] +default = ["std"] +std = [ + "serde", + "codec/std", + "sp-std/std", + "cfg-traits/std", + "frame-support/std", + "frame-system/std", + "scale-info/std", + "orml-xtokens/std", + "orml-traits/std", + "xcm/std" +] + +runtime-benchmarks = [ + "cfg-traits/runtime-benchmarks", + "frame-support/runtime-benchmarks", + "frame-system/runtime-benchmarks", + "orml-xtokens/runtime-benchmarks", +] +try-runtime = [ + "cfg-traits/try-runtime", + "frame-support/try-runtime", + "frame-system/try-runtime", + "orml-xtokens/try-runtime", +] diff --git a/pallets/restricted-xtokens/src/lib.rs b/pallets/restricted-xtokens/src/lib.rs new file mode 100644 index 0000000000..a9451793eb --- /dev/null +++ b/pallets/restricted-xtokens/src/lib.rs @@ -0,0 +1,401 @@ +// Copyright 2023 Centrifuge Foundation (centrifuge.io). +// +// This file is part of the Centrifuge chain project. +// Centrifuge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version (see http://www.gnu.org/licenses). +// Centrifuge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +//! # Pallet-Restricted-Xtokens Module +//! +//! ## Overview +//! +//! This pallet is a wrapper pallet over `orml-xtokens` and allows the runtime +//! to create arbitrary filters for transfers of x-chain-transfers. +//! The interface mimic 1-to-1 the interface of `orml-xtokens`. +//! +//! ## Interface +//! +//! ### Dispatchable functions +//! +//! - `transfer`: Transfer local assets with given `CurrencyId` and `Amount`. +//! - `transfer_multiasset`: Transfer `MultiAsset` assets. +//! - `transfer_with_fee`: Transfer native currencies specifying the fee and +//! amount as separate. +//! - `transfer_multiasset_with_fee`: Transfer `MultiAsset` specifying the fee +//! and amount as separate. +//! - `transfer_multicurrencies`: Transfer several currencies specifying the +//! item to be used as fee. +//! - `transfer_multiassets`: Transfer several `MultiAsset` specifying the item +//! to be used as fee. + +#![cfg_attr(not(feature = "std"), no_std)] +#![allow(clippy::from_over_into)] +#![allow(clippy::unused_unit)] +#![allow(clippy::large_enum_variant)] +#![allow(clippy::boxed_local)] +#![allow(clippy::too_many_arguments)] + +use cfg_traits::PreConditions; +use frame_support::pallet_prelude::*; +use frame_system::{ensure_signed, pallet_prelude::*}; +use orml_traits::XtokensWeightInfo; +pub use pallet::*; +use sp_std::{boxed::Box, vec::Vec}; +use xcm::{v3::prelude::*, VersionedMultiAsset, VersionedMultiAssets, VersionedMultiLocation}; + +pub enum TransferEffects { + Transfer { + sender: AccountId, + destination: MultiLocation, + currency_id: CurrencyId, + amount: Balance, + }, + TransferMultiAsset { + sender: AccountId, + destination: MultiLocation, + asset: MultiAsset, + }, + TransferWithFee { + sender: AccountId, + destination: MultiLocation, + currency_id: CurrencyId, + amount: Balance, + fee: Balance, + }, + TransferMultiAssetWithFee { + sender: AccountId, + destination: MultiLocation, + asset: MultiAsset, + fee_asset: MultiAsset, + }, + TransferMultiCurrencies { + sender: AccountId, + destination: MultiLocation, + currencies: Vec<(CurrencyId, Balance)>, + fee: (CurrencyId, Balance), + }, + TransferMultiAssets { + sender: AccountId, + destination: MultiLocation, + assets: MultiAssets, + fee_asset: MultiAsset, + }, +} + +#[frame_support::pallet] +pub mod pallet { + use super::*; + + #[pallet::config] + pub trait Config: frame_system::Config + orml_xtokens::Config { + type PreTransfer: PreConditions< + TransferEffects, + Result = DispatchResult, + >; + } + + #[pallet::pallet] + pub struct Pallet(_); + + #[pallet::error] + pub enum Error { + BadVersion, + } + + #[pallet::call] + impl Pallet { + /// Transfer native currencies. + /// + /// `dest_weight_limit` is the weight for XCM execution on the dest + /// chain, and it would be charged from the transferred assets. If set + /// below requirements, the execution may fail and assets wouldn't be + /// received. + /// + /// It's a no-op if any error on local XCM execution or message sending. + /// Note sending assets out per se doesn't guarantee they would be + /// received. Receiving depends on if the XCM message could be delivered + /// by the network, and if the receiving chain would handle + /// messages correctly. + #[pallet::call_index(0)] + #[pallet::weight(orml_xtokens::XtokensWeight::< T >::weight_of_transfer(currency_id.clone(), * amount, dest) + T::DbWeight::get().reads(4))] + pub fn transfer( + origin: OriginFor, + currency_id: T::CurrencyId, + amount: T::Balance, + dest: Box, + dest_weight_limit: WeightLimit, + ) -> DispatchResult { + let destination: MultiLocation = (*dest.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let sender = ensure_signed(origin.clone())?; + + T::PreTransfer::check(TransferEffects::Transfer { + sender, + destination, + currency_id: currency_id.clone(), + amount, + })?; + + orml_xtokens::Pallet::::transfer( + origin, + currency_id, + amount, + dest, + dest_weight_limit, + ) + } + + /// Transfer `MultiAsset`. + /// + /// `dest_weight_limit` is the weight for XCM execution on the dest + /// chain, and it would be charged from the transferred assets. If set + /// below requirements, the execution may fail and assets wouldn't be + /// received. + /// + /// It's a no-op if any error on local XCM execution or message sending. + /// Note sending assets out per se doesn't guarantee they would be + /// received. Receiving depends on if the XCM message could be delivered + /// by the network, and if the receiving chain would handle + /// messages correctly. + #[pallet::call_index(1)] + #[pallet::weight(orml_xtokens::XtokensWeight::< T >::weight_of_transfer_multiasset(asset, dest) + T::DbWeight::get().reads(4))] + pub fn transfer_multiasset( + origin: OriginFor, + asset: Box, + dest: Box, + dest_weight_limit: WeightLimit, + ) -> DispatchResult { + let sender = ensure_signed(origin.clone())?; + let multi_asset: MultiAsset = (*asset.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let destination: MultiLocation = (*dest.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + + T::PreTransfer::check(TransferEffects::TransferMultiAsset { + sender, + destination, + asset: multi_asset, + })?; + + orml_xtokens::Pallet::::transfer_multiasset(origin, asset, dest, dest_weight_limit) + } + + /// Transfer native currencies specifying the fee and amount as + /// separate. + /// + /// `dest_weight_limit` is the weight for XCM execution on the dest + /// chain, and it would be charged from the transferred assets. If set + /// below requirements, the execution may fail and assets wouldn't be + /// received. + /// + /// `fee` is the amount to be spent to pay for execution in destination + /// chain. Both fee and amount will be subtracted form the callers + /// balance. + /// + /// If `fee` is not high enough to cover for the execution costs in the + /// destination chain, then the assets will be trapped in the + /// destination chain + /// + /// It's a no-op if any error on local XCM execution or message sending. + /// Note sending assets out per se doesn't guarantee they would be + /// received. Receiving depends on if the XCM message could be delivered + /// by the network, and if the receiving chain would handle + /// messages correctly. + #[pallet::call_index(2)] + #[pallet::weight(orml_xtokens::XtokensWeight::< T >::weight_of_transfer(currency_id.clone(), * amount, dest) + T::DbWeight::get().reads(4))] + pub fn transfer_with_fee( + origin: OriginFor, + currency_id: T::CurrencyId, + amount: T::Balance, + fee: T::Balance, + dest: Box, + dest_weight_limit: WeightLimit, + ) -> DispatchResult { + let sender = ensure_signed(origin.clone())?; + let destination: MultiLocation = (*dest.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + + T::PreTransfer::check(TransferEffects::TransferWithFee { + sender, + destination, + currency_id: currency_id.clone(), + amount, + fee, + })?; + + orml_xtokens::Pallet::::transfer_with_fee( + origin, + currency_id, + amount, + fee, + dest, + dest_weight_limit, + ) + } + + /// Transfer `MultiAsset` specifying the fee and amount as separate. + /// + /// `dest_weight_limit` is the weight for XCM execution on the dest + /// chain, and it would be charged from the transferred assets. If set + /// below requirements, the execution may fail and assets wouldn't be + /// received. + /// + /// `fee` is the multiasset to be spent to pay for execution in + /// destination chain. Both fee and amount will be subtracted form the + /// callers balance For now we only accept fee and asset having the same + /// `MultiLocation` id. + /// + /// If `fee` is not high enough to cover for the execution costs in the + /// destination chain, then the assets will be trapped in the + /// destination chain + /// + /// It's a no-op if any error on local XCM execution or message sending. + /// Note sending assets out per se doesn't guarantee they would be + /// received. Receiving depends on if the XCM message could be delivered + /// by the network, and if the receiving chain would handle + /// messages correctly. + #[pallet::call_index(3)] + #[pallet::weight(orml_xtokens::XtokensWeight::< T >::weight_of_transfer_multiasset(asset, dest) + T::DbWeight::get().reads(4))] + pub fn transfer_multiasset_with_fee( + origin: OriginFor, + asset: Box, + fee: Box, + dest: Box, + dest_weight_limit: WeightLimit, + ) -> DispatchResult { + let sender = ensure_signed(origin.clone())?; + let multi_asset: MultiAsset = (*asset.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let fee_asset: MultiAsset = (*fee.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let destination: MultiLocation = (*dest.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + + T::PreTransfer::check(TransferEffects::TransferMultiAssetWithFee { + sender, + destination, + asset: multi_asset, + fee_asset, + })?; + + orml_xtokens::Pallet::::transfer_multiasset_with_fee( + origin, + asset, + fee, + dest, + dest_weight_limit, + ) + } + + /// Transfer several currencies specifying the item to be used as fee + /// + /// `dest_weight_limit` is the weight for XCM execution on the dest + /// chain, and it would be charged from the transferred assets. If set + /// below requirements, the execution may fail and assets wouldn't be + /// received. + /// + /// `fee_item` is index of the currencies tuple that we want to use for + /// payment + /// + /// It's a no-op if any error on local XCM execution or message sending. + /// Note sending assets out per se doesn't guarantee they would be + /// received. Receiving depends on if the XCM message could be delivered + /// by the network, and if the receiving chain would handle + /// messages correctly. + #[pallet::call_index(4)] + #[pallet::weight(orml_xtokens::XtokensWeight::< T >::weight_of_transfer_multicurrencies(currencies, fee_item, dest) + T::DbWeight::get().reads(4))] + pub fn transfer_multicurrencies( + origin: OriginFor, + currencies: Vec<(T::CurrencyId, T::Balance)>, + fee_item: u32, + dest: Box, + dest_weight_limit: WeightLimit, + ) -> DispatchResult { + let sender = ensure_signed(origin.clone())?; + let destination: MultiLocation = (*dest.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let fee = currencies + .get(fee_item as usize) + .ok_or(orml_xtokens::Error::::AssetIndexNonExistent)?; + + T::PreTransfer::check(TransferEffects::TransferMultiCurrencies { + sender, + destination, + currencies: currencies.clone(), + fee: fee.clone(), + })?; + + orml_xtokens::Pallet::::transfer_multicurrencies( + origin, + currencies, + fee_item, + dest, + dest_weight_limit, + ) + } + + /// Transfer several `MultiAsset` specifying the item to be used as fee + /// + /// `dest_weight_limit` is the weight for XCM execution on the dest + /// chain, and it would be charged from the transferred assets. If set + /// below requirements, the execution may fail and assets wouldn't be + /// received. + /// + /// `fee_item` is index of the MultiAssets that we want to use for + /// payment + /// + /// It's a no-op if any error on local XCM execution or message sending. + /// Note sending assets out per se doesn't guarantee they would be + /// received. Receiving depends on if the XCM message could be delivered + /// by the network, and if the receiving chain would handle + /// messages correctly. + #[pallet::call_index(5)] + #[pallet::weight(orml_xtokens::XtokensWeight::< T >::weight_of_transfer_multiassets(assets, fee_item, dest) + T::DbWeight::get().reads(4))] + pub fn transfer_multiassets( + origin: OriginFor, + assets: Box, + fee_item: u32, + dest: Box, + dest_weight_limit: WeightLimit, + ) -> DispatchResult { + let sender = ensure_signed(origin.clone())?; + let multi_assets: MultiAssets = (*assets.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let destination: MultiLocation = (*dest.clone()) + .try_into() + .map_err(|()| Error::::BadVersion)?; + let fee_asset: &MultiAsset = multi_assets + .get(fee_item as usize) + .ok_or(orml_xtokens::Error::::AssetIndexNonExistent)?; + + T::PreTransfer::check(TransferEffects::TransferMultiAssets { + sender, + destination, + assets: multi_assets.clone(), + fee_asset: fee_asset.clone(), + })?; + + orml_xtokens::Pallet::::transfer_multiassets( + origin, + assets, + fee_item, + dest, + dest_weight_limit, + ) + } + } +} diff --git a/pallets/transfer-allowlist/Cargo.toml b/pallets/transfer-allowlist/Cargo.toml index 9e48946b01..bb1e0315ba 100644 --- a/pallets/transfer-allowlist/Cargo.toml +++ b/pallets/transfer-allowlist/Cargo.toml @@ -29,14 +29,12 @@ sp-runtime = { git = "https://github.com/paritytech/substrate", default-features sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.43" } [dev-dependencies] -cfg-mocks = { path = "../../libs/mocks" } pallet-balances = { git = "https://github.com/paritytech/substrate", default-features = true, branch = "polkadot-v0.9.43" } sp-io = { git = "https://github.com/paritytech/substrate", default-features = true, branch = "polkadot-v0.9.43" } [features] default = ['std'] runtime-benchmarks = [ - 'cfg-mocks/runtime-benchmarks', 'cfg-traits/runtime-benchmarks', 'cfg-types/runtime-benchmarks', 'frame-benchmarking/runtime-benchmarks', diff --git a/pallets/transfer-allowlist/src/benchmarking.rs b/pallets/transfer-allowlist/src/benchmarking.rs index 62b6fbfd9d..213e1fe507 100644 --- a/pallets/transfer-allowlist/src/benchmarking.rs +++ b/pallets/transfer-allowlist/src/benchmarking.rs @@ -12,22 +12,23 @@ #![cfg(feature = "runtime-benchmarks")] -use cfg_traits::fees::Fees; +use cfg_types::tokens::CurrencyId; use codec::EncodeLike; use frame_benchmarking::*; -use frame_support::traits::{Currency, Get, ReservableCurrency}; +use frame_support::{ + pallet_prelude::Get, + traits::{fungible::Unbalanced, tokens::Precision, Currency, ReservableCurrency}, +}; use frame_system::RawOrigin; use scale_info::TypeInfo; -use sp_runtime::traits::{AtLeast32BitUnsigned, Bounded, CheckedAdd, One}; +use sp_runtime::{ + traits::{AtLeast32BitUnsigned, Bounded, CheckedAdd, One}, + Saturating, +}; use super::*; -#[cfg(test)] -fn config_mocks() { - use crate::mock::Fees; - Fees::mock_fee_value(|_| 0); - Fees::mock_fee_to_author(|_, _| Ok(())); -} +const BENCHMARK_CURRENCY_ID: CurrencyId = CurrencyId::ForeignAsset(1); benchmarks! { where_clause { @@ -35,110 +36,117 @@ benchmarks! { ::AccountId: Into<::Location>, ::Location: From<::AccountId> + EncodeLike<::Location>, ::ReserveCurrency: Currency<::AccountId> + ReservableCurrency<::AccountId>, - ::CurrencyId: Default, - ::BlockNumber: AtLeast32BitUnsigned + Bounded + TypeInfo - + T: pallet::Config, + ::BlockNumber: AtLeast32BitUnsigned + Bounded + TypeInfo, + <::ReserveCurrency as frame_support::traits::fungible::Inspect<::AccountId,>>::Balance: From } add_transfer_allowance_no_existing_metadata { let (sender, receiver) = set_up_users::(); - }:add_transfer_allowance(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), receiver.clone().into()) + }:add_transfer_allowance(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, receiver.clone().into()) add_transfer_allowance_existing_metadata { let (sender, receiver) = set_up_users::(); - Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), 200u32.into())?; - }:add_transfer_allowance(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), receiver.clone().into()) + Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, 200u32.into())?; + }:add_transfer_allowance(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, receiver.clone().into()) add_allowance_delay_no_existing_metadata { let (sender, receiver) = set_up_users::(); - }:add_allowance_delay(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), 200u32.into()) + }:add_allowance_delay(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, 200u32.into()) add_allowance_delay_existing_metadata { let (sender, receiver) = set_up_users::(); - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - }:add_allowance_delay(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), 200u32.into()) + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + }:add_allowance_delay(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, 200u32.into()) toggle_allowance_delay_once_future_modifiable { let (sender, receiver) = set_up_users::(); - Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), 1u32.into())?; - }:toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()), T::CurrencyId::default()) + Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, 1u32.into())?; + }:toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID) update_allowance_delay { let (sender, receiver) = set_up_users::(); - Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), 1u32.into())?; - Pallet::::toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default())?; + Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, 1u32.into())?; + Pallet::::toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID)?; let b = frame_system::Pallet::::block_number() .checked_add(&1u32.into()) .expect("Mock block advancement failed."); frame_system::Pallet::::set_block_number(b); - }:update_allowance_delay(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), 200u32.into()) + }:update_allowance_delay(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, 200u32.into()) purge_allowance_delay_no_remaining_metadata { let (sender, receiver) = set_up_users::(); - Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), 1u32.into())?; - Pallet::::toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default())?; + Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, 1u32.into())?; + Pallet::::toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID)?; let b = frame_system::Pallet::::block_number() .checked_add(&2u32.into()) .expect("Mock block advancement failed."); frame_system::Pallet::::set_block_number(b); - }:purge_allowance_delay(RawOrigin::Signed(sender.clone()), T::CurrencyId::default()) + }:purge_allowance_delay(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID) purge_allowance_delay_remaining_metadata { let (sender, receiver) = set_up_users::(); - Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), 1u32.into())?; - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - Pallet::::toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default())?; + Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, 1u32.into())?; + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + Pallet::::toggle_allowance_delay_once_future_modifiable(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID)?; let b = frame_system::Pallet::::block_number() .checked_add(&2u32.into()) .expect("Mock block advancement failed."); frame_system::Pallet::::set_block_number(b); - }:purge_allowance_delay(RawOrigin::Signed(sender.clone()), T::CurrencyId::default()) + }:purge_allowance_delay(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID) remove_transfer_allowance_delay_present { let (sender, receiver) = set_up_users::(); let delay = T::BlockNumber::one(); - Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), delay.clone())?; - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; + Pallet::::add_allowance_delay(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, delay.clone())?; + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; let b = frame_system::Pallet::::block_number() .checked_add(&1u32.into()) .expect("Mock block advancement failed."); frame_system::Pallet::::set_block_number(b); - }:remove_transfer_allowance(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), receiver.clone().into()) + }:remove_transfer_allowance(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, receiver.clone().into()) remove_transfer_allowance_no_delay { let (sender, receiver) = set_up_users::(); - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - }:remove_transfer_allowance(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), receiver.clone().into()) + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + }:remove_transfer_allowance(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, receiver.clone().into()) purge_transfer_allowance_no_remaining_metadata { let (sender, receiver) = set_up_users::(); - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - Pallet::::remove_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - }:purge_transfer_allowance(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), receiver.clone().into()) + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + Pallet::::remove_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + }:purge_transfer_allowance(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, receiver.clone().into()) purge_transfer_allowance_remaining_metadata { let (sender, receiver) = set_up_users::(); let receiver_1 = set_up_second_receiver::(); - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver_1.clone().into())?; - Pallet::::remove_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), T::CurrencyId::default(), receiver.clone().into())?; - }:purge_transfer_allowance(RawOrigin::Signed(sender.clone()), T::CurrencyId::default(), receiver.clone().into()) + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + Pallet::::add_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver_1.clone().into())?; + Pallet::::remove_transfer_allowance(RawOrigin::Signed(sender.clone()).into(), BENCHMARK_CURRENCY_ID, receiver.clone().into())?; + }:purge_transfer_allowance(RawOrigin::Signed(sender.clone()), BENCHMARK_CURRENCY_ID, receiver.clone().into()) } -fn set_up_users() -> (T::AccountId, T::AccountId) { - #[cfg(test)] - config_mocks(); +fn set_up_users() -> (T::AccountId, T::AccountId) +where + <::ReserveCurrency as frame_support::traits::fungible::Inspect< + ::AccountId, + >>::Balance: From, +{ let sender: T::AccountId = account::("Sender", 1, 0); let receiver: T::AccountId = account::("Receiver", 2, 0); - T::ReserveCurrency::deposit_creating( + + T::ReserveCurrency::increase_balance( &sender, - T::Fees::fee_value(T::AllowanceFeeKey::get()) * 4u32.into(), - ); + T::Deposit::get().saturating_mul(10.into()), + Precision::BestEffort, + ) + .expect("sender account balance can be increased"); + (sender, receiver) } diff --git a/pallets/transfer-allowlist/src/lib.rs b/pallets/transfer-allowlist/src/lib.rs index c8793a35f3..abfb141a11 100644 --- a/pallets/transfer-allowlist/src/lib.rs +++ b/pallets/transfer-allowlist/src/lib.rs @@ -40,11 +40,14 @@ pub use weights::WeightInfo; pub mod pallet { use core::fmt::Debug; - use cfg_traits::fees::Fees; use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use frame_support::{ pallet_prelude::{DispatchResult, Member, OptionQuery, StorageDoubleMap, StorageNMap, *}, - traits::{tokens::AssetId, Currency, ReservableCurrency}, + traits::{ + fungible, + fungible::MutateHold, + tokens::{AssetId, Precision}, + }, Twox64Concat, }; use frame_system::pallet_prelude::{OriginFor, *}; @@ -57,13 +60,18 @@ pub mod pallet { use super::*; /// Balance type for the reserve/deposit made when creating an Allowance - pub type DepositBalanceOf = <::ReserveCurrency as Currency< + pub type DepositBalanceOf = <::ReserveCurrency as fungible::Inspect< ::AccountId, >>::Balance; /// AllowanceDetails where `BlockNumber` is of type T::BlockNumber pub type AllowanceDetailsOf = AllowanceDetails<::BlockNumber>; + /// Resons for holding as defined by the fungible::hold::Inspect trait + pub type ReasonOf = <::ReserveCurrency as fungible::hold::Inspect< + ::AccountId, + >>::Reason; + /// The current storage version. const STORAGE_VERSION: StorageVersion = StorageVersion::new(0); @@ -76,32 +84,23 @@ pub mod pallet { pub trait Config: frame_system::Config { type RuntimeEvent: From> + IsType<::RuntimeEvent>; - /// Id type of Currency transfer restrictions will be checked for - type CurrencyId: AssetId - + Parameter - + Debug - + Default - + Member - + Copy - + MaybeSerializeDeserialize - + Ord - + TypeInfo - + MaxEncodedLen; + type CurrencyId: AssetId + Parameter + Member + Copy + Default; + + // We need this to block restrictions on the native chain currency as + // currently it is impossible to place transfer restrictions + // on native currencies as we simply have no way of + // restricting pallet-balances... + type NativeCurrency: Get; - /// Currency for Reserve/Unreserve with allowlist adding/removal, + /// Currency for holding/unholding with allowlist adding/removal, /// given that the allowlist will be in storage - type ReserveCurrency: Currency + ReservableCurrency; + type ReserveCurrency: fungible::hold::Mutate; - /// Fee handler for the reserve/unreserve - /// Currently just stores the amounts, will be extended to handle - /// reserve/unreserve as well in future - type Fees: Fees< - AccountId = ::AccountId, - Balance = DepositBalanceOf, - >; + /// The identifier to be used for holding. + type HoldId: Get>; - /// Fee Key used to find amount for allowance reserve/unreserve - type AllowanceFeeKey: Get<::FeeKey>; + /// Deposit amount + type Deposit: Get>; /// Type containing the locations a transfer can be sent to. type Location: Member @@ -257,6 +256,8 @@ pub mod pallet { /// Transfer from sending account and currency not allowed to /// destination NoAllowanceForDestination, + /// Native currency can not be restricted with allowances + NativeCurrencyNotRestrictable, } #[pallet::event] @@ -331,6 +332,11 @@ pub mod pallet { ) -> DispatchResult { let account_id = ensure_signed(origin)?; + ensure!( + currency_id != T::NativeCurrency::get(), + Error::::NativeCurrencyNotRestrictable + ); + let allowance_details = match Self::get_account_currency_restriction_count_delay( &account_id, currency_id, @@ -351,10 +357,7 @@ pub mod pallet { &receiver, )) { Self::increment_or_create_allowance_count(&account_id, ¤cy_id)?; - T::ReserveCurrency::reserve( - &account_id, - T::Fees::fee_value(T::AllowanceFeeKey::get()), - )?; + T::ReserveCurrency::hold(&T::HoldId::get(), &account_id, T::Deposit::get())?; }; >::insert( (&account_id, ¤cy_id, &receiver), @@ -433,10 +436,12 @@ pub mod pallet { match >::get((&account_id, ¤cy_id, &receiver)) { Some(AllowanceDetails { blocked_at, .. }) if blocked_at < current_block => { - T::ReserveCurrency::unreserve( + T::ReserveCurrency::release( + &T::HoldId::get(), &account_id, - T::Fees::fee_value(T::AllowanceFeeKey::get()), - ); + T::Deposit::get(), + Precision::BestEffort, + )?; >::remove(( &account_id, ¤cy_id, diff --git a/pallets/transfer-allowlist/src/mock.rs b/pallets/transfer-allowlist/src/mock.rs index 7cc718c3c2..4541769887 100644 --- a/pallets/transfer-allowlist/src/mock.rs +++ b/pallets/transfer-allowlist/src/mock.rs @@ -10,7 +10,7 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -use cfg_mocks::pallet_mock_fees; +use cfg_types::tokens::CurrencyId; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ parameter_types, @@ -30,8 +30,6 @@ pub(crate) const STARTING_BLOCK: u64 = 50; pub(crate) const SENDER: u64 = 0x1; pub(crate) const ACCOUNT_RECEIVER: u64 = 0x2; pub(crate) const FEE_DEFICIENT_SENDER: u64 = 0x3; -pub(crate) const ALLOWANCE_FEE_AMOUNT: u64 = 10u64; -pub(crate) const ALLOWANCE_FEEKEY: u8 = 0u8; type Balance = u64; type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; @@ -44,9 +42,8 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic, { - Balances: pallet_balances, - Fees: pallet_mock_fees, - System: frame_system, + Balances: pallet_balances, + System: frame_system, TransferAllowList: transfer_allowlist, } ); @@ -83,43 +80,6 @@ impl frame_system::Config for Runtime { type Version = (); } -impl pallet_mock_fees::Config for Runtime { - type Balance = Balance; - type FeeKey = u8; -} - -parameter_types! { - pub const DefaultFeeValue: Balance = 1; -} - -#[derive( - Clone, - Copy, - Debug, - PartialOrd, - Ord, - Encode, - Decode, - Eq, - PartialEq, - MaxEncodedLen, - TypeInfo, - Deserialize, - Serialize, -)] -pub enum CurrencyId { - A, - B, - C, - D, -} - -impl Default for CurrencyId { - fn default() -> Self { - Self::A - } -} - #[derive( Clone, Debug, @@ -162,14 +122,16 @@ impl pallet_balances::Config for Runtime { } parameter_types! { - pub const TransferAllowlistFeeKey: u8 = ALLOWANCE_FEEKEY; + pub const NativeCurrency: CurrencyId = CurrencyId::Native; + pub const HoldId: () = (); } impl transfer_allowlist::Config for Runtime { - type AllowanceFeeKey = TransferAllowlistFeeKey; type CurrencyId = CurrencyId; - type Fees = Fees; + type Deposit = ConstU64<10>; + type HoldId = HoldId; type Location = Location; + type NativeCurrency = NativeCurrency; type ReserveCurrency = Balances; type RuntimeEvent = RuntimeEvent; type WeightInfo = (); @@ -190,12 +152,8 @@ pub fn new_test_ext() -> sp_io::TestExternalities { e.execute_with(|| { System::set_block_number(STARTING_BLOCK); - - Fees::mock_fee_value(|key| match key { - ALLOWANCE_FEEKEY => ALLOWANCE_FEE_AMOUNT, - _ => panic!("No valid fee key"), - }); }); + e } diff --git a/pallets/transfer-allowlist/src/tests.rs b/pallets/transfer-allowlist/src/tests.rs index 0ad27f5d14..7be3a58d50 100644 --- a/pallets/transfer-allowlist/src/tests.rs +++ b/pallets/transfer-allowlist/src/tests.rs @@ -1,20 +1,53 @@ +use cfg_types::tokens::CurrencyId; use frame_support::{assert_err, assert_noop, assert_ok}; use super::*; use crate::mock::*; +const TEST_CURRENCY_ID: CurrencyId = CurrencyId::ForeignAsset(1); + +#[test] +fn add_transfer_fails_for_native() { + new_test_ext().execute_with(|| { + assert_noop!( + TransferAllowList::add_transfer_allowance( + RuntimeOrigin::signed(SENDER), + ::NativeCurrency::get(), + ACCOUNT_RECEIVER.into(), + ), + Error::::NativeCurrencyNotRestrictable + ); + assert_eq!( + TransferAllowList::get_account_currency_transfer_allowance(( + SENDER, + TEST_CURRENCY_ID, + ::Location::from(ACCOUNT_RECEIVER) + )), + None, + ); + assert_eq!( + TransferAllowList::get_account_currency_restriction_count_delay( + SENDER, + TEST_CURRENCY_ID + ), + None, + ); + + assert_eq!(Balances::reserved_balance(&SENDER), 0); + }) +} #[test] fn add_transfer_allowance_works() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(ACCOUNT_RECEIVER) )) .unwrap(), @@ -26,7 +59,7 @@ fn add_transfer_allowance_works() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 1, @@ -35,21 +68,7 @@ fn add_transfer_allowance_works() { }) ); - assert_eq!( - System::events()[0].event, - RuntimeEvent::Balances(pallet_balances::Event::Reserved { who: 1, amount: 10 }) - ); assert_eq!(Balances::reserved_balance(&SENDER), 10); - assert_eq!( - System::events()[1].event, - RuntimeEvent::TransferAllowList(Event::TransferAllowanceCreated { - sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), - receiver: ::Location::from(ACCOUNT_RECEIVER), - allowed_at: 0, - blocked_at: u64::MAX - }) - ) }) } @@ -58,17 +77,17 @@ fn add_transfer_allowance_updates_with_delay_set() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200 )); assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), ),); @@ -77,7 +96,7 @@ fn add_transfer_allowance_updates_with_delay_set() { assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(ACCOUNT_RECEIVER) )) .unwrap(), @@ -91,7 +110,7 @@ fn add_transfer_allowance_updates_with_delay_set() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ) .unwrap(), AllowanceMetadata { @@ -108,13 +127,13 @@ fn add_transfer_allowance_multiple_dests_increments_correctly() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!(Balances::reserved_balance(&SENDER), 10); assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 100u64.into(), )); // verify reserve incremented for second allowance @@ -122,7 +141,7 @@ fn add_transfer_allowance_multiple_dests_increments_correctly() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 2, @@ -130,15 +149,6 @@ fn add_transfer_allowance_multiple_dests_increments_correctly() { once_modifiable_after: None }) ); - - assert_eq!( - System::events()[0].event, - RuntimeEvent::Balances(pallet_balances::Event::Reserved { who: 1, amount: 10 }) - ); - assert_eq!( - System::events()[2].event, - RuntimeEvent::Balances(pallet_balances::Event::Reserved { who: 1, amount: 10 }) - ); }) } @@ -147,15 +157,11 @@ fn transfer_allowance_allows_correctly_with_allowance_set() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!( - TransferAllowList::allowance( - SENDER.into(), - ACCOUNT_RECEIVER.into(), - ::CurrencyId::default() - ), + TransferAllowList::allowance(SENDER.into(), ACCOUNT_RECEIVER.into(), TEST_CURRENCY_ID), Ok(()) ) }) @@ -166,15 +172,11 @@ fn transfer_allowance_blocks_when_account_not_allowed() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_err!( - TransferAllowList::allowance( - SENDER.into(), - 55u64.into(), - ::CurrencyId::default() - ), + TransferAllowList::allowance(SENDER.into(), 55u64.into(), TEST_CURRENCY_ID), Error::::NoAllowanceForDestination, ) }) @@ -185,21 +187,17 @@ fn transfer_allowance_blocks_correctly_when_before_start_block() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 10u64.into() )); assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_err!( - TransferAllowList::allowance( - SENDER.into(), - ACCOUNT_RECEIVER.into(), - ::CurrencyId::default() - ), + TransferAllowList::allowance(SENDER.into(), ACCOUNT_RECEIVER.into(), TEST_CURRENCY_ID), Error::::NoAllowanceForDestination, ) }) @@ -210,15 +208,11 @@ fn transfer_allowance_blocks_correctly_when_after_blocked_at_block() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!( - TransferAllowList::allowance( - SENDER.into(), - ACCOUNT_RECEIVER.into(), - ::CurrencyId::default() - ), + TransferAllowList::allowance(SENDER.into(), ACCOUNT_RECEIVER.into(), TEST_CURRENCY_ID), Ok(()) ) }) @@ -229,19 +223,19 @@ fn remove_transfer_allowance_works() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_ok!(TransferAllowList::remove_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); // ensure blocked at set to restrict transfers assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(ACCOUNT_RECEIVER) )) .unwrap(), @@ -260,7 +254,7 @@ fn remove_transfer_allowance_works() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 1, @@ -268,18 +262,6 @@ fn remove_transfer_allowance_works() { once_modifiable_after: None }) ); - - // event 1 for allowance creation itelf - assert_eq!( - System::events()[2].event, - RuntimeEvent::TransferAllowList(Event::TransferAllowanceRemoved { - sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), - receiver: ::Location::from(ACCOUNT_RECEIVER), - allowed_at: 0u64, - blocked_at: 50u64 - }) - ) }) } @@ -288,23 +270,23 @@ fn remove_transfer_allowance_with_delay_works() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64.into() )); assert_ok!(TransferAllowList::remove_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(ACCOUNT_RECEIVER) )) .unwrap(), @@ -319,7 +301,7 @@ fn remove_transfer_allowance_with_delay_works() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 1, @@ -330,19 +312,6 @@ fn remove_transfer_allowance_with_delay_works() { // ensure only 1 reserve as we've still just got 1 allowance in storage assert_eq!(Balances::reserved_balance(&SENDER), 10); - - // 0, allowance creation itself - // 1, delay creation - assert_eq!( - System::events()[3].event, - RuntimeEvent::TransferAllowList(Event::TransferAllowanceRemoved { - sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), - receiver: ::Location::from(ACCOUNT_RECEIVER), - allowed_at: 0u64, - blocked_at: 250u64 - }) - ) }) } @@ -352,18 +321,18 @@ fn purge_transfer_allowance_works() { // Add delay to ensure blocked_at is not set to MAX assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 5u64 )); // create allowance to test removal assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_ok!(TransferAllowList::remove_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!(Balances::reserved_balance(&SENDER), 10); @@ -372,14 +341,14 @@ fn purge_transfer_allowance_works() { // test removal assert_ok!(TransferAllowList::purge_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); // verify removed assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(ACCOUNT_RECEIVER) )), None @@ -392,7 +361,7 @@ fn purge_transfer_allowance_works() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 0, @@ -400,25 +369,6 @@ fn purge_transfer_allowance_works() { once_modifiable_after: None }) ); - // verify event sent for removal - // event 0 is delay, addition to ensure blocked at set - // event 1 is reserve - // event 2 is allowance creation - // Event 3 is allowance removal to set blocked at - // event 4 is unreserve from purge - // event 5 is purge - assert_eq!( - System::events()[4].event, - RuntimeEvent::Balances(pallet_balances::Event::Unreserved { who: 1, amount: 10 }) - ); - assert_eq!( - System::events()[5].event, - RuntimeEvent::TransferAllowList(Event::TransferAllowancePurged { - sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), - receiver: ::Location::from(ACCOUNT_RECEIVER), - }) - ); }) } #[test] @@ -428,7 +378,7 @@ fn purge_transfer_allowance_non_existant_transfer_allowance() { assert_noop!( TransferAllowList::purge_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), ), Error::::NoMatchingAllowance @@ -442,26 +392,26 @@ fn purge_transfer_allowance_when_multiple_present_for_sender_currency_properly_d // Add delay to ensure blocked_at is not set to MAX assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 5u64 )); // add multiple entries for sender/currency to test dec assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); assert_eq!(Balances::reserved_balance(&SENDER), 10); assert_ok!(TransferAllowList::add_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 100u64.into(), )); assert_eq!(Balances::reserved_balance(&SENDER), 20); assert_ok!(TransferAllowList::remove_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); @@ -470,7 +420,7 @@ fn purge_transfer_allowance_when_multiple_present_for_sender_currency_properly_d // test removal assert_ok!(TransferAllowList::purge_transfer_allowance( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, ACCOUNT_RECEIVER.into(), )); @@ -481,7 +431,7 @@ fn purge_transfer_allowance_when_multiple_present_for_sender_currency_properly_d assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(ACCOUNT_RECEIVER) )), None @@ -491,7 +441,7 @@ fn purge_transfer_allowance_when_multiple_present_for_sender_currency_properly_d assert_eq!( TransferAllowList::get_account_currency_transfer_allowance(( SENDER, - ::CurrencyId::default(), + TEST_CURRENCY_ID, ::Location::from(100u64) )) .unwrap(), @@ -505,7 +455,7 @@ fn purge_transfer_allowance_when_multiple_present_for_sender_currency_properly_d assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 1, @@ -522,14 +472,14 @@ fn add_allowance_delay_works() { // verify extrinsic execution returns ok assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); // verify val in storage assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 0, @@ -542,7 +492,7 @@ fn add_allowance_delay_works() { System::events()[0].event, RuntimeEvent::TransferAllowList(Event::TransferAllowanceDelayAdd { sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), + currency_id: TEST_CURRENCY_ID, delay: 200 }) ) @@ -554,13 +504,13 @@ fn cannot_create_conflicint_allowance_delays() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); assert_noop!( TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 250u64 ), Error::::DuplicateDelay @@ -569,7 +519,7 @@ fn cannot_create_conflicint_allowance_delays() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 0, @@ -582,7 +532,7 @@ fn cannot_create_conflicint_allowance_delays() { System::events()[0].event, RuntimeEvent::TransferAllowList(Event::TransferAllowanceDelayAdd { sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), + currency_id: TEST_CURRENCY_ID, delay: 200 }) ) @@ -594,13 +544,13 @@ fn set_allowance_delay_future_modifiable_works() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); @@ -608,7 +558,7 @@ fn set_allowance_delay_future_modifiable_works() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 0, @@ -624,7 +574,7 @@ fn set_allowance_delay_future_modifiable_works() { System::events()[1].event, RuntimeEvent::TransferAllowList(Event::ToggleTransferAllowanceDelayFutureModifiable { sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), + currency_id: TEST_CURRENCY_ID, modifiable_once_after: Some(250) }) ) @@ -636,13 +586,13 @@ fn set_allowance_delay_future_modifiable_fails_if_modifiable_set_and_not_reached new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); advance_n_blocks::(20); @@ -650,7 +600,7 @@ fn set_allowance_delay_future_modifiable_fails_if_modifiable_set_and_not_reached assert_noop!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ), Error::::DelayUnmodifiable ); @@ -662,13 +612,13 @@ fn set_allowance_delay_future_modifiable_works_if_modifiable_set_and_reached() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); advance_n_blocks::(200); @@ -676,14 +626,14 @@ fn set_allowance_delay_future_modifiable_works_if_modifiable_set_and_reached() { assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); // verify val in storage assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 0, @@ -700,7 +650,7 @@ fn set_allowance_delay_future_modifiable_works_if_modifiable_set_and_reached() { System::events()[2].event, RuntimeEvent::TransferAllowList(Event::ToggleTransferAllowanceDelayFutureModifiable { sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), + currency_id: TEST_CURRENCY_ID, modifiable_once_after: None }) ) @@ -712,19 +662,19 @@ fn purge_allowance_delay_works() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); advance_n_blocks::(201); assert_ok!(TransferAllowList::purge_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID )); // note: @@ -734,14 +684,14 @@ fn purge_allowance_delay_works() { System::events()[2].event, RuntimeEvent::TransferAllowList(Event::TransferAllowanceDelayPurge { sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), + currency_id: TEST_CURRENCY_ID, }) ); assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), None ) @@ -753,13 +703,13 @@ fn purge_allowance_delay_fails_if_not_set_modifiable() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); assert_noop!( TransferAllowList::purge_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ), Error::::DelayUnmodifiable ); @@ -771,28 +721,28 @@ fn purge_allowance_delay_fails_if_modifiable_at_not_reached() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 )); // verify can't be removed before setting future modifiable assert_noop!( TransferAllowList::purge_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ), Error::::DelayUnmodifiable ); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); // verify can't remove before modifiable_at reached assert_noop!( TransferAllowList::purge_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ), Error::::DelayUnmodifiable ); @@ -800,7 +750,7 @@ fn purge_allowance_delay_fails_if_modifiable_at_not_reached() { assert_noop!( TransferAllowList::purge_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ), Error::::DelayUnmodifiable ); @@ -813,7 +763,7 @@ fn update_allowance_delay_fails_if_no_delay() { assert_noop!( TransferAllowList::update_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 200u64 ), Error::::NoMatchingDelay @@ -826,14 +776,14 @@ fn update_allowance_delay_fails_if_modifiable_after_not_set() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 10u64 )); advance_n_blocks::(15); assert_noop!( TransferAllowList::update_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 20 ), Error::::DelayUnmodifiable @@ -846,20 +796,20 @@ fn update_allowance_delay_fails_if_modifiable_after_not_reached() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 20u64 )); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); advance_n_blocks::(15); assert_noop!( TransferAllowList::update_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 20 ), Error::::DelayUnmodifiable @@ -872,20 +822,20 @@ fn update_allowance_delay_works() { new_test_ext().execute_with(|| { assert_ok!(TransferAllowList::add_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 10u64 )); assert_ok!( TransferAllowList::toggle_allowance_delay_once_future_modifiable( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default() + TEST_CURRENCY_ID ) ); advance_n_blocks::(12); assert_ok!(TransferAllowList::update_allowance_delay( RuntimeOrigin::signed(SENDER), - ::CurrencyId::default(), + TEST_CURRENCY_ID, 20 )); @@ -893,7 +843,7 @@ fn update_allowance_delay_works() { assert_eq!( TransferAllowList::get_account_currency_restriction_count_delay( SENDER, - ::CurrencyId::default() + TEST_CURRENCY_ID ), Some(AllowanceMetadata { allowance_count: 0, @@ -910,7 +860,7 @@ fn update_allowance_delay_works() { System::events()[2].event, RuntimeEvent::TransferAllowList(Event::TransferAllowanceDelayUpdate { sender_account_id: SENDER, - currency_id: ::CurrencyId::default(), + currency_id: TEST_CURRENCY_ID, delay: 20 }) ) diff --git a/runtime/altair/Cargo.toml b/runtime/altair/Cargo.toml index 94b46ff52e..9731dd1f38 100644 --- a/runtime/altair/Cargo.toml +++ b/runtime/altair/Cargo.toml @@ -124,6 +124,7 @@ pallet-pool-system = { workspace = true } pallet-preimage = { workspace = true } pallet-proxy = { workspace = true } pallet-restricted-tokens = { workspace = true } +pallet-restricted-xtokens = { workspace = true } pallet-rewards = { workspace = true } pallet-scheduler = { workspace = true } pallet-session = { workspace = true } @@ -252,6 +253,7 @@ std = [ "pallet-preimage/std", "pallet-proxy/std", "pallet-restricted-tokens/std", + "pallet-restricted-xtokens/std", "pallet-rewards/std", "pallet-scheduler/std", "pallet-session/std", @@ -338,6 +340,7 @@ runtime-benchmarks = [ "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-restricted-tokens/runtime-benchmarks", + "pallet-restricted-xtokens/runtime-benchmarks", "pallet-rewards/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", @@ -425,6 +428,7 @@ try-runtime = [ "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-restricted-tokens/try-runtime", + "pallet-restricted-xtokens/try-runtime", "pallet-rewards/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", diff --git a/runtime/altair/src/lib.rs b/runtime/altair/src/lib.rs index 13eb7d5c60..e94f3debc4 100644 --- a/runtime/altair/src/lib.rs +++ b/runtime/altair/src/lib.rs @@ -80,7 +80,7 @@ use runtime_common::{ fees::{DealWithFees, WeightToFee}, production_or_benchmark, xcm::AccountIdToMultiLocation, - xcm_transactor, CurrencyED, + xcm_transactor, AllowanceDeposit, CurrencyED, HoldId, NativeCurrency, }; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; @@ -437,6 +437,7 @@ pub enum ProxyType { PodOperation, PodAuth, PermissionManagement, + Transfer, } impl Default for ProxyType { fn default() -> Self { @@ -581,6 +582,18 @@ impl InstanceFilter for ProxyType { RuntimeCall::Permissions(pallet_permissions::Call::add { .. }) | RuntimeCall::Permissions(pallet_permissions::Call::remove { .. }) ), + ProxyType::Transfer => { + matches!( + c, + RuntimeCall::XTokens(..) + | RuntimeCall::Balances(..) + | RuntimeCall::Tokens(..) + | RuntimeCall::LiquidityPools( + pallet_liquidity_pools::Call::transfer { .. } + | pallet_liquidity_pools::Call::transfer_tranche_tokens { .. } + ) + ) + } } } @@ -1104,7 +1117,7 @@ impl pallet_restricted_tokens::Config for Runtime { type NativeFungible = Balances; type NativeToken = NativeToken; type PreCurrency = cfg_traits::Always; - type PreExtrTransfer = cfg_traits::Always; + type PreExtrTransfer = PreNativeTransfer; type PreFungibleInspect = FungibleInspectPassthrough; type PreFungibleInspectHold = cfg_traits::Always; type PreFungibleMutate = cfg_traits::Always; @@ -1728,6 +1741,17 @@ impl pallet_order_book::Config for Runtime { type Weights = weights::pallet_order_book::WeightInfo; } +impl pallet_transfer_allowlist::Config for Runtime { + type CurrencyId = CurrencyId; + type Deposit = AllowanceDeposit; + type HoldId = HoldId; + type Location = Location; + type NativeCurrency = NativeCurrency; + type ReserveCurrency = Balances; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_transfer_allowlist::WeightInfo; +} + // Frame Order in this block dictates the index of each one in the metadata // Any addition should be done at the bottom // Any deletion affects the following frames during runtime upgrades @@ -1794,14 +1818,16 @@ construct_runtime!( GapRewardMechanism: pallet_rewards::mechanism::gap = 112, OrderBook: pallet_order_book::{Pallet, Call, Storage, Event} = 113, ForeignInvestments: pallet_foreign_investments::{Pallet, Storage, Event} = 114, + TransferAllowList: pallet_transfer_allowlist::{Pallet, Call, Storage, Event} = 115, // XCM XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 120, PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Config, Event, Origin} = 121, CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 122, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 123, - XTokens: orml_xtokens::{Pallet, Storage, Call, Event} = 124, + XTokens: pallet_restricted_xtokens::{Pallet, Call} = 124, XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Storage, Event} = 125, + OrmlXTokens: orml_xtokens::{Pallet, Event} = 126, // 3rd party pallets OrmlTokens: orml_tokens::{Pallet, Storage, Event, Config} = 150, @@ -1961,6 +1987,8 @@ mod __runtime_api_use { #[cfg(not(feature = "disable-runtime-api"))] use __runtime_api_use::*; +use cfg_types::locations::Location; +use runtime_common::transfer_filter::PreNativeTransfer; #[cfg(not(feature = "disable-runtime-api"))] impl_runtime_apis! { @@ -2464,6 +2492,7 @@ impl_runtime_apis! { list_benchmark!(list, extra, pallet_xcm, PolkadotXcm); list_benchmark!(list, extra, cumulus_pallet_xcmp_queue, XcmpQueue); list_benchmark!(list, extra, pallet_liquidity_rewards, LiquidityRewards); + list_benchmark!(list, extra, pallet_transfer_allowlist, TransferAllowList); let storage_info = AllPalletsWithSystem::storage_info(); @@ -2540,6 +2569,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_xcm, PolkadotXcm); add_benchmark!(params, batches, cumulus_pallet_xcmp_queue, XcmpQueue); add_benchmark!(params, batches, pallet_liquidity_rewards, LiquidityRewards); + add_benchmark!(params, batches, pallet_transfer_allowlist, TransferAllowList); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/runtime/altair/src/liquidity_pools.rs b/runtime/altair/src/liquidity_pools.rs index 334a131ef1..eab19f9303 100644 --- a/runtime/altair/src/liquidity_pools.rs +++ b/runtime/altair/src/liquidity_pools.rs @@ -25,13 +25,14 @@ use pallet_liquidity_pools::hooks::{ use runtime_common::{ account_conversion::AccountConverter, foreign_investments::IdentityPoolCurrencyConverter, gateway::GatewayAccountProvider, liquidity_pools::LiquidityPoolsMessage, + transfer_filter::PreLpTransfer, }; use sp_runtime::traits::One; use crate::{ ForeignInvestments, Investments, LiquidityPools, LiquidityPoolsGateway, LocationToAccountId, OrderBook, OrmlAssetRegistry, Permissions, PoolSystem, Runtime, RuntimeEvent, RuntimeOrigin, - Timestamp, Tokens, TreasuryAccount, + Timestamp, Tokens, TransferAllowList, TreasuryAccount, }; parameter_types! { @@ -76,6 +77,7 @@ impl pallet_liquidity_pools::Config for Runtime { type Permission = Permissions; type PoolId = PoolId; type PoolInspect = PoolSystem; + type PreTransferFilter = PreLpTransfer; type RuntimeEvent = RuntimeEvent; type Time = Timestamp; type Tokens = Tokens; diff --git a/runtime/altair/src/weights/mod.rs b/runtime/altair/src/weights/mod.rs index b877d56c0b..ad02471eeb 100644 --- a/runtime/altair/src/weights/mod.rs +++ b/runtime/altair/src/weights/mod.rs @@ -46,3 +46,5 @@ pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_vesting; pub mod pallet_xcm; + +pub mod pallet_transfer_allowlist; diff --git a/runtime/altair/src/weights/pallet_transfer_allowlist.rs b/runtime/altair/src/weights/pallet_transfer_allowlist.rs new file mode 100644 index 0000000000..b9eb3b75ba --- /dev/null +++ b/runtime/altair/src/weights/pallet_transfer_allowlist.rs @@ -0,0 +1,214 @@ + +//! Autogenerated weights for `pallet_transfer_allowlist` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `kf-FG`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("development-local"), DB CACHE: 1024 + +// Executed Command: +// target/release/centrifuge-chain +// benchmark +// pallet +// --chain=development-local +// --steps=50 +// --repeat=20 +// --pallet=pallet_transfer_allowlist +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=/tmp/runtime/development/src/weights/pallet_transfer_allowlist.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transfer_allowlist`. +pub struct WeightInfo(PhantomData); +impl pallet_transfer_allowlist::WeightInfo for WeightInfo { + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn add_transfer_allowance_no_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `537` + // Estimated: `3596` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(40_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn add_transfer_allowance_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `626` + // Estimated: `3596` + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(43_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn add_allowance_delay_no_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3556` + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn add_allowance_delay_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `299` + // Estimated: `3556` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn toggle_allowance_delay_once_future_modifiable() -> Weight { + // Proof Size summary in bytes: + // Measured: `269` + // Estimated: `3556` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn update_allowance_delay() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3556` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_allowance_delay_no_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3556` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_allowance_delay_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `307` + // Estimated: `3556` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:0) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + fn remove_transfer_allowance_delay_present() -> Weight { + // Proof Size summary in bytes: + // Measured: `397` + // Estimated: `3596` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:0) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + fn remove_transfer_allowance_no_delay() -> Weight { + // Proof Size summary in bytes: + // Measured: `393` + // Estimated: `3596` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(21_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_transfer_allowance_no_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `750` + // Estimated: `3596` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_transfer_allowance_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `789` + // Estimated: `3596` + // Minimum execution time: 40_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + + fn remove_transfer_allowance_missing_allowance() -> Weight { + Weight::from_parts(21_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/runtime/altair/src/xcm.rs b/runtime/altair/src/xcm.rs index 63f9517c47..746475aad0 100644 --- a/runtime/altair/src/xcm.rs +++ b/runtime/altair/src/xcm.rs @@ -32,6 +32,7 @@ use orml_xcm_support::MultiNativeAsset; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use runtime_common::{ + transfer_filter::PreXcmTransfer, xcm::{general_key, AccountIdToMultiLocation, FixedConversionRateProvider}, xcm_fees::native_per_second, }; @@ -390,6 +391,10 @@ impl orml_xtokens::Config for Runtime { type XcmExecutor = XcmExecutor; } +impl pallet_restricted_xtokens::Config for Runtime { + type PreTransfer = PreXcmTransfer; +} + impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; diff --git a/runtime/centrifuge/Cargo.toml b/runtime/centrifuge/Cargo.toml index a08abe9119..6420331c01 100644 --- a/runtime/centrifuge/Cargo.toml +++ b/runtime/centrifuge/Cargo.toml @@ -124,6 +124,7 @@ pallet-pool-system = { workspace = true } pallet-preimage = { workspace = true } pallet-proxy = { workspace = true } pallet-restricted-tokens = { workspace = true } +pallet-restricted-xtokens = { workspace = true } pallet-rewards = { workspace = true } pallet-scheduler = { workspace = true } pallet-session = { workspace = true } @@ -252,6 +253,7 @@ std = [ "pallet-preimage/std", "pallet-proxy/std", "pallet-restricted-tokens/std", + "pallet-restricted-xtokens/std", "pallet-rewards/std", "pallet-scheduler/std", "pallet-session/std", @@ -337,6 +339,7 @@ runtime-benchmarks = [ "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-restricted-tokens/runtime-benchmarks", + "pallet-restricted-xtokens/runtime-benchmarks", "pallet-rewards/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", @@ -424,6 +427,7 @@ try-runtime = [ "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-restricted-tokens/try-runtime", + "pallet-restricted-xtokens/try-runtime", "pallet-rewards/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", diff --git a/runtime/centrifuge/src/lib.rs b/runtime/centrifuge/src/lib.rs index 7567379f76..2c1cedfca8 100644 --- a/runtime/centrifuge/src/lib.rs +++ b/runtime/centrifuge/src/lib.rs @@ -80,7 +80,8 @@ use pallet_transaction_payment_rpc_runtime_api::{FeeDetails, RuntimeDispatchInfo use polkadot_runtime_common::{prod_or_fast, BlockHashCount, SlowAdjustingFeeUpdate}; use runtime_common::{ account_conversion::AccountConverter, asset_registry, production_or_benchmark, - xcm::AccountIdToMultiLocation, xcm_transactor, CurrencyED, + xcm::AccountIdToMultiLocation, xcm_transactor, AllowanceDeposit, CurrencyED, HoldId, + NativeCurrency, }; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; @@ -328,7 +329,10 @@ impl pallet_restricted_tokens::Config for Runtime { type NativeFungible = Balances; type NativeToken = NativeToken; type PreCurrency = cfg_traits::Always; - type PreExtrTransfer = RestrictedTokens; + type PreExtrTransfer = ( + RestrictedTokens, + PreNativeTransfer, + ); type PreFungibleInspect = FungibleInspectPassthrough; type PreFungibleInspectHold = cfg_traits::Always; type PreFungibleMutate = cfg_traits::Always; @@ -582,6 +586,7 @@ pub enum ProxyType { PodOperation, PodAuth, PermissionManagement, + Transfer, } impl Default for ProxyType { fn default() -> Self { @@ -724,6 +729,18 @@ impl InstanceFilter for ProxyType { | RuntimeCall::Utility(pallet_utility::Call::batch_all { .. }) | RuntimeCall::Utility(pallet_utility::Call::batch { .. }) ), + ProxyType::Transfer => { + matches!( + c, + RuntimeCall::XTokens(..) + | RuntimeCall::Balances(..) + | RuntimeCall::Tokens(..) + | RuntimeCall::LiquidityPools( + pallet_liquidity_pools::Call::transfer { .. } + | pallet_liquidity_pools::Call::transfer_tranche_tokens { .. } + ) + ) + } } } @@ -1832,6 +1849,17 @@ impl pallet_order_book::Config for Runtime { type Weights = weights::pallet_order_book::WeightInfo; } +impl pallet_transfer_allowlist::Config for Runtime { + type CurrencyId = CurrencyId; + type Deposit = AllowanceDeposit; + type HoldId = HoldId; + type Location = Location; + type NativeCurrency = NativeCurrency; + type ReserveCurrency = Balances; + type RuntimeEvent = RuntimeEvent; + type WeightInfo = weights::pallet_transfer_allowlist::WeightInfo; +} + // Frame Order in this block dictates the index of each one in the metadata // Any addition should be done at the bottom // Any deletion affects the following frames during runtime upgrades @@ -1893,14 +1921,16 @@ construct_runtime!( LiquidityPoolsGateway: pallet_liquidity_pools_gateway::{Pallet, Call, Storage, Event, Origin } = 107, OrderBook: pallet_order_book::{Pallet, Call, Storage, Event} = 108, ForeignInvestments: pallet_foreign_investments::{Pallet, Storage, Event} = 109, + TransferAllowList: pallet_transfer_allowlist::{Pallet, Call, Storage, Event} = 110, // XCM XcmpQueue: cumulus_pallet_xcmp_queue::{Pallet, Call, Storage, Event} = 120, PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Config, Event, Origin} = 121, CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 122, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 123, - XTokens: orml_xtokens::{Pallet, Storage, Call, Event} = 124, + XTokens: pallet_restricted_xtokens::{Pallet, Call} = 124, XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Storage, Event} = 125, + OrmlXTokens: orml_xtokens::{Pallet, Event} = 126, // 3rd party pallets ChainBridge: chainbridge::{Pallet, Call, Storage, Event} = 150, @@ -1998,6 +2028,8 @@ mod __runtime_api_use { #[cfg(not(feature = "disable-runtime-api"))] use __runtime_api_use::*; +use cfg_types::locations::Location; +use runtime_common::transfer_filter::PreNativeTransfer; #[cfg(not(feature = "disable-runtime-api"))] impl_runtime_apis! { @@ -2503,6 +2535,7 @@ impl_runtime_apis! { list_benchmark!(list, extra, pallet_investments, Investments); list_benchmark!(list, extra, pallet_xcm, PolkadotXcm); list_benchmark!(list, extra, pallet_liquidity_rewards, LiquidityRewards); + list_benchmark!(list, extra, pallet_transfer_allowlist, TransferAllowList); let storage_info = AllPalletsWithSystem::storage_info(); @@ -2577,6 +2610,7 @@ impl_runtime_apis! { add_benchmark!(params, batches, pallet_investments, Investments); add_benchmark!(params, batches, pallet_xcm, PolkadotXcm); add_benchmark!(params, batches, pallet_liquidity_rewards, LiquidityRewards); + add_benchmark!(params, batches, pallet_transfer_allowlist, TransferAllowList); if batches.is_empty() { return Err("Benchmark not found for this pallet.".into()) } Ok(batches) diff --git a/runtime/centrifuge/src/liquidity_pools.rs b/runtime/centrifuge/src/liquidity_pools.rs index 8c8cda6959..3085b6d434 100644 --- a/runtime/centrifuge/src/liquidity_pools.rs +++ b/runtime/centrifuge/src/liquidity_pools.rs @@ -25,14 +25,15 @@ use pallet_liquidity_pools::hooks::{ use runtime_common::{ account_conversion::AccountConverter, foreign_investments::IdentityPoolCurrencyConverter, gateway::GatewayAccountProvider, liquidity_pools::LiquidityPoolsMessage, - origin::EnsureAccountOrRootOr, + origin::EnsureAccountOrRootOr, transfer_filter::PreLpTransfer, }; use sp_runtime::traits::One; use crate::{ ForeignInvestments, Investments, LiquidityPools, LiquidityPoolsAxelarGateway, LiquidityPoolsGateway, LocationToAccountId, OrderBook, OrmlAssetRegistry, Permissions, - PoolSystem, Runtime, RuntimeEvent, RuntimeOrigin, Timestamp, Tokens, TreasuryAccount, + PoolSystem, Runtime, RuntimeEvent, RuntimeOrigin, Timestamp, Tokens, TransferAllowList, + TreasuryAccount, }; parameter_types! { @@ -65,8 +66,6 @@ parameter_types! { } impl pallet_liquidity_pools::Config for Runtime { - // NOTE: No need to adapt that. The Router is an artifact and will be removed - // with FI PR type AdminOrigin = EnsureRootOr; type AssetRegistry = OrmlAssetRegistry; type Balance = Balance; @@ -80,6 +79,7 @@ impl pallet_liquidity_pools::Config for Runtime { type Permission = Permissions; type PoolId = PoolId; type PoolInspect = PoolSystem; + type PreTransferFilter = PreLpTransfer; type RuntimeEvent = RuntimeEvent; type Time = Timestamp; type Tokens = Tokens; diff --git a/runtime/centrifuge/src/weights/mod.rs b/runtime/centrifuge/src/weights/mod.rs index 27802fd7ce..9bbc05b537 100644 --- a/runtime/centrifuge/src/weights/mod.rs +++ b/runtime/centrifuge/src/weights/mod.rs @@ -45,3 +45,5 @@ pub mod pallet_uniques; pub mod pallet_utility; pub mod pallet_vesting; pub mod pallet_xcm; + +pub mod pallet_transfer_allowlist; diff --git a/runtime/centrifuge/src/weights/pallet_transfer_allowlist.rs b/runtime/centrifuge/src/weights/pallet_transfer_allowlist.rs new file mode 100644 index 0000000000..b9eb3b75ba --- /dev/null +++ b/runtime/centrifuge/src/weights/pallet_transfer_allowlist.rs @@ -0,0 +1,214 @@ + +//! Autogenerated weights for `pallet_transfer_allowlist` +//! +//! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev +//! DATE: 2023-11-10, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! WORST CASE MAP SIZE: `1000000` +//! HOSTNAME: `kf-FG`, CPU: `` +//! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("development-local"), DB CACHE: 1024 + +// Executed Command: +// target/release/centrifuge-chain +// benchmark +// pallet +// --chain=development-local +// --steps=50 +// --repeat=20 +// --pallet=pallet_transfer_allowlist +// --extrinsic=* +// --execution=wasm +// --wasm-execution=compiled +// --heap-pages=4096 +// --output=/tmp/runtime/development/src/weights/pallet_transfer_allowlist.rs + +#![cfg_attr(rustfmt, rustfmt_skip)] +#![allow(unused_parens)] +#![allow(unused_imports)] +#![allow(missing_docs)] + +use frame_support::{traits::Get, weights::Weight}; +use core::marker::PhantomData; + +/// Weight functions for `pallet_transfer_allowlist`. +pub struct WeightInfo(PhantomData); +impl pallet_transfer_allowlist::WeightInfo for WeightInfo { + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn add_transfer_allowance_no_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `537` + // Estimated: `3596` + // Minimum execution time: 39_000_000 picoseconds. + Weight::from_parts(40_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + fn add_transfer_allowance_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `626` + // Estimated: `3596` + // Minimum execution time: 42_000_000 picoseconds. + Weight::from_parts(43_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn add_allowance_delay_no_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `180` + // Estimated: `3556` + // Minimum execution time: 11_000_000 picoseconds. + Weight::from_parts(12_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn add_allowance_delay_existing_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `299` + // Estimated: `3556` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn toggle_allowance_delay_once_future_modifiable() -> Weight { + // Proof Size summary in bytes: + // Measured: `269` + // Estimated: `3556` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn update_allowance_delay() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3556` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_allowance_delay_no_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `273` + // Estimated: `3556` + // Minimum execution time: 13_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_allowance_delay_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `307` + // Estimated: `3556` + // Minimum execution time: 14_000_000 picoseconds. + Weight::from_parts(14_000_000, 0) + .saturating_add(Weight::from_parts(0, 3556)) + .saturating_add(T::DbWeight::get().reads(1)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:0) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + fn remove_transfer_allowance_delay_present() -> Weight { + // Proof Size summary in bytes: + // Measured: `397` + // Estimated: `3596` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(22_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:0) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + fn remove_transfer_allowance_no_delay() -> Weight { + // Proof Size summary in bytes: + // Measured: `393` + // Estimated: `3596` + // Minimum execution time: 21_000_000 picoseconds. + Weight::from_parts(21_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_transfer_allowance_no_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `750` + // Estimated: `3596` + // Minimum execution time: 41_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + /// Storage: TransferAllowList AccountCurrencyTransferAllowance (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferAllowance (max_values: None, max_size: Some(131), added: 2606, mode: MaxEncodedLen) + /// Storage: Fees FeeBalances (r:1 w:0) + /// Proof: Fees FeeBalances (max_values: None, max_size: Some(48), added: 2523, mode: MaxEncodedLen) + /// Storage: System Account (r:1 w:1) + /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) + /// Storage: TransferAllowList AccountCurrencyTransferCountDelay (r:1 w:1) + /// Proof: TransferAllowList AccountCurrencyTransferCountDelay (max_values: None, max_size: Some(91), added: 2566, mode: MaxEncodedLen) + fn purge_transfer_allowance_remaining_metadata() -> Weight { + // Proof Size summary in bytes: + // Measured: `789` + // Estimated: `3596` + // Minimum execution time: 40_000_000 picoseconds. + Weight::from_parts(42_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(4)) + .saturating_add(T::DbWeight::get().writes(3)) + } + + fn remove_transfer_allowance_missing_allowance() -> Weight { + Weight::from_parts(21_000_000, 0) + .saturating_add(Weight::from_parts(0, 3596)) + .saturating_add(T::DbWeight::get().reads(2)) + .saturating_add(T::DbWeight::get().writes(1)) + } +} diff --git a/runtime/centrifuge/src/xcm.rs b/runtime/centrifuge/src/xcm.rs index 8f7f98f7ac..c847fd1e56 100644 --- a/runtime/centrifuge/src/xcm.rs +++ b/runtime/centrifuge/src/xcm.rs @@ -34,6 +34,7 @@ use orml_xcm_support::MultiNativeAsset; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use runtime_common::{ + transfer_filter::PreXcmTransfer, xcm::{general_key, AccountIdToMultiLocation, FixedConversionRateProvider, LpInstanceRelayer}, xcm_fees::native_per_second, }; @@ -412,6 +413,10 @@ impl orml_xtokens::Config for Runtime { type XcmExecutor = XcmExecutor; } +impl pallet_restricted_xtokens::Config for Runtime { + type PreTransfer = PreXcmTransfer; +} + impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; diff --git a/runtime/common/Cargo.toml b/runtime/common/Cargo.toml index d7d76cdc15..60e899c3ee 100644 --- a/runtime/common/Cargo.toml +++ b/runtime/common/Cargo.toml @@ -102,6 +102,7 @@ pallet-pool-system = { workspace = true } pallet-preimage = { workspace = true } pallet-proxy = { workspace = true } pallet-restricted-tokens = { workspace = true } +pallet-restricted-xtokens = { workspace = true } pallet-rewards = { workspace = true } pallet-scheduler = { workspace = true } pallet-session = { workspace = true } @@ -206,6 +207,7 @@ std = [ "pallet-preimage/std", "pallet-proxy/std", "pallet-restricted-tokens/std", + "pallet-restricted-xtokens/std", "pallet-rewards/std", "pallet-scheduler/std", "pallet-session/std", @@ -359,6 +361,7 @@ try-runtime = [ "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-restricted-tokens/try-runtime", + "pallet-restricted-xtokens/try-runtime", "pallet-rewards/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", diff --git a/runtime/common/src/lib.rs b/runtime/common/src/lib.rs index 87ccff7b17..c2392b7205 100644 --- a/runtime/common/src/lib.rs +++ b/runtime/common/src/lib.rs @@ -23,14 +23,38 @@ pub mod fees; pub mod gateway; pub mod migrations; pub mod oracle; +pub mod transfer_filter; pub mod xcm; use cfg_primitives::Balance; -use cfg_types::tokens::CurrencyId; +use cfg_types::{fee_keys::FeeKey, tokens::CurrencyId}; use orml_traits::GetByKey; +use sp_core::parameter_types; use sp_runtime::traits::Get; use sp_std::marker::PhantomData; +parameter_types! { + /// The native currency identifier of our currency id enum + /// to be used for Get types. + pub const NativeCurrency: CurrencyId = CurrencyId::Native; + + /// The hold identifier in our system to be used for + /// Get<()> types + pub const HoldId: HoldIdentifier = (); +} + +pub struct AllowanceDeposit(sp_std::marker::PhantomData); +impl> Get + for AllowanceDeposit +{ + fn get() -> Balance { + T::fee_value(FeeKey::AllowanceCreation) + } +} + +/// To be used with the transfer-allowlist pallet across runtimes +pub type HoldIdentifier = (); + #[macro_export] macro_rules! production_or_benchmark { ($production:expr, $benchmark:expr) => {{ diff --git a/runtime/common/src/transfer_filter.rs b/runtime/common/src/transfer_filter.rs new file mode 100644 index 0000000000..2275d49b9d --- /dev/null +++ b/runtime/common/src/transfer_filter.rs @@ -0,0 +1,144 @@ +// Copyright 2023 Centrifuge Foundation (centrifuge.io). +// +// This file is part of the Centrifuge chain project. +// Centrifuge is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version (see http://www.gnu.org/licenses). +// Centrifuge is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +use cfg_primitives::{AccountId, Balance}; +use cfg_traits::{PreConditions, TransferAllowance}; +use cfg_types::{domain_address::DomainAddress, locations::Location, tokens::CurrencyId}; +use codec::Encode; +use pallet_restricted_tokens::TransferDetails; +use pallet_restricted_xtokens::TransferEffects; +use sp_core::Hasher; +use sp_runtime::{ + traits::{BlakeTwo256, Convert}, + DispatchError, DispatchResult, TokenError, +}; +use xcm::v3::{MultiAsset, MultiLocation}; + +pub struct PreXcmTransfer(sp_std::marker::PhantomData<(T, C)>); + +impl< + T: TransferAllowance, + C: Convert>, + > PreConditions> for PreXcmTransfer +{ + type Result = DispatchResult; + + fn check(t: TransferEffects) -> Self::Result { + let currency_based_check = |sender, destination: MultiLocation, currency| { + T::allowance( + sender, + Location::XCM(BlakeTwo256::hash(&destination.encode())), + currency, + ) + }; + + let asset_based_check = |sender, destination, asset| { + let currency = + C::convert(asset).ok_or(DispatchError::Token(TokenError::UnknownAsset))?; + + currency_based_check(sender, destination, currency) + }; + + match t { + TransferEffects::Transfer { + sender, + destination, + currency_id, + .. + } => currency_based_check(sender, destination, currency_id), + TransferEffects::TransferMultiAsset { + sender, + destination, + asset, + } => asset_based_check(sender, destination, asset), + TransferEffects::TransferWithFee { + sender, + destination, + currency_id, + .. + } => currency_based_check(sender, destination, currency_id), + TransferEffects::TransferMultiAssetWithFee { + sender, + destination, + asset, + fee_asset, + } => { + asset_based_check(sender.clone(), destination, asset)?; + + // NOTE: We do check the fee asset and assume that the destination + // is the same as for the actual assets. This is a pure subjective + // security assumption to not allow randomly burning fees of + // protected assets. + asset_based_check(sender, destination, fee_asset) + } + TransferEffects::TransferMultiCurrencies { + sender, + destination, + currencies, + fee, + } => { + for (currency, ..) in currencies { + currency_based_check(sender.clone(), destination, currency)?; + } + + // NOTE: We do check the fee asset and assume that the destination + // is the same as for the actual assets. This is a pure subjective + // security assumption to not allow randomly burning fees of + // protected assets. + currency_based_check(sender, destination, fee.0) + } + TransferEffects::TransferMultiAssets { + sender, + destination, + assets, + fee_asset, + } => { + // NOTE: We do not check the fee, as we assume, that this is not a transfer + // but rather a burn of tokens. Furthermore, we do not know the + // destination where those fees will go. + for asset in assets.into_inner() { + asset_based_check(sender.clone(), destination, asset)?; + } + + // NOTE: We do check the fee asset and assume that the destination + // is the same as for the actual assets. This is a pure subjective + // security assumption to not allow randomly burning fees of + // protected assets. + asset_based_check(sender, destination, fee_asset) + } + } + } +} + +pub struct PreNativeTransfer(sp_std::marker::PhantomData); + +impl> + PreConditions> for PreNativeTransfer +{ + type Result = bool; + + fn check(t: TransferDetails) -> Self::Result { + T::allowance(t.send, Location::Local(t.recv), t.id).is_ok() + } +} +pub struct PreLpTransfer(sp_std::marker::PhantomData); + +impl> + PreConditions<(AccountId, DomainAddress, CurrencyId)> for PreLpTransfer +{ + type Result = DispatchResult; + + fn check(t: (AccountId, DomainAddress, CurrencyId)) -> Self::Result { + let (sender, receiver, currency) = t; + T::allowance(sender, Location::Address(receiver), currency) + } +} diff --git a/runtime/development/Cargo.toml b/runtime/development/Cargo.toml index 397cd27a90..9b4e40a774 100644 --- a/runtime/development/Cargo.toml +++ b/runtime/development/Cargo.toml @@ -123,6 +123,7 @@ pallet-pool-system = { workspace = true } pallet-preimage = { workspace = true } pallet-proxy = { workspace = true } pallet-restricted-tokens = { workspace = true } +pallet-restricted-xtokens = { workspace = true } pallet-rewards = { workspace = true } pallet-scheduler = { workspace = true } pallet-session = { workspace = true } @@ -250,6 +251,7 @@ std = [ "pallet-preimage/std", "pallet-proxy/std", "pallet-restricted-tokens/std", + "pallet-restricted-xtokens/std", "pallet-rewards/std", "pallet-scheduler/std", "pallet-session/std", @@ -336,6 +338,7 @@ runtime-benchmarks = [ "pallet-preimage/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-restricted-tokens/runtime-benchmarks", + "pallet-restricted-xtokens/runtime-benchmarks", "pallet-rewards/runtime-benchmarks", "pallet-scheduler/runtime-benchmarks", "pallet-sudo/runtime-benchmarks", @@ -423,6 +426,7 @@ try-runtime = [ "pallet-preimage/try-runtime", "pallet-proxy/try-runtime", "pallet-restricted-tokens/try-runtime", + "pallet-restricted-xtokens/try-runtime", "pallet-rewards/try-runtime", "pallet-scheduler/try-runtime", "pallet-session/try-runtime", diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index a1bfca89cd..1f132d1651 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -93,7 +93,7 @@ use runtime_common::{ fees::{DealWithFees, WeightToFee}, production_or_benchmark, xcm::AccountIdToMultiLocation, - xcm_transactor, CurrencyED, + xcm_transactor, AllowanceDeposit, CurrencyED, HoldId, NativeCurrency, }; use scale_info::TypeInfo; use sp_api::impl_runtime_apis; @@ -448,6 +448,7 @@ pub enum ProxyType { PodOperation, PodAuth, PermissionManagement, + Transfer, } impl Default for ProxyType { fn default() -> Self { @@ -596,6 +597,18 @@ impl InstanceFilter for ProxyType { RuntimeCall::Permissions(pallet_permissions::Call::add { .. }) | RuntimeCall::Permissions(pallet_permissions::Call::remove { .. }) ), + ProxyType::Transfer => { + matches!( + c, + RuntimeCall::XTokens(..) + | RuntimeCall::Balances(..) + | RuntimeCall::Tokens(..) + | RuntimeCall::LiquidityPools( + pallet_liquidity_pools::Call::transfer { .. } + | pallet_liquidity_pools::Call::transfer_tranche_tokens { .. } + ) + ) + } } } @@ -1502,7 +1515,10 @@ impl pallet_restricted_tokens::Config for Runtime { type NativeFungible = Balances; type NativeToken = NativeToken; type PreCurrency = cfg_traits::Always; - type PreExtrTransfer = RestrictedTokens; + type PreExtrTransfer = ( + RestrictedTokens, + PreNativeTransfer, + ); type PreFungibleInspect = FungibleInspectPassthrough; type PreFungibleInspectHold = cfg_traits::Always; type PreFungibleMutate = cfg_traits::Always; @@ -1796,15 +1812,12 @@ impl pallet_block_rewards::Config for Runtime { type WeightInfo = weights::pallet_block_rewards::WeightInfo; } -parameter_types! { - pub const TransferAllowlistFeeKey: FeeKey = FeeKey::AllowanceCreation; -} - impl pallet_transfer_allowlist::Config for Runtime { - type AllowanceFeeKey = TransferAllowlistFeeKey; type CurrencyId = CurrencyId; - type Fees = Fees; + type Deposit = AllowanceDeposit; + type HoldId = HoldId; type Location = Location; + type NativeCurrency = NativeCurrency; type ReserveCurrency = Balances; type RuntimeEvent = RuntimeEvent; type WeightInfo = weights::pallet_transfer_allowlist::WeightInfo; @@ -1908,8 +1921,9 @@ construct_runtime!( PolkadotXcm: pallet_xcm::{Pallet, Call, Storage, Config, Event, Origin} = 121, CumulusXcm: cumulus_pallet_xcm::{Pallet, Event, Origin} = 122, DmpQueue: cumulus_pallet_dmp_queue::{Pallet, Call, Storage, Event} = 123, - XTokens: orml_xtokens::{Pallet, Storage, Call, Event} = 124, + XTokens: pallet_restricted_xtokens::{Pallet, Call} = 124, XcmTransactor: pallet_xcm_transactor::{Pallet, Call, Storage, Event} = 125, + OrmlXTokens: orml_xtokens::{Pallet, Event} = 126, // 3rd party pallets OrmlTokens: orml_tokens::{Pallet, Storage, Event, Config} = 150, @@ -2089,6 +2103,7 @@ mod __runtime_api_use { #[cfg(not(feature = "disable-runtime-api"))] use __runtime_api_use::*; +use runtime_common::transfer_filter::PreNativeTransfer; #[cfg(not(feature = "disable-runtime-api"))] impl_runtime_apis! { diff --git a/runtime/development/src/liquidity_pools.rs b/runtime/development/src/liquidity_pools.rs index 470042c221..8f433b7814 100644 --- a/runtime/development/src/liquidity_pools.rs +++ b/runtime/development/src/liquidity_pools.rs @@ -26,13 +26,15 @@ use pallet_liquidity_pools::hooks::{ use runtime_common::{ account_conversion::AccountConverter, foreign_investments::IdentityPoolCurrencyConverter, gateway::GatewayAccountProvider, liquidity_pools::LiquidityPoolsMessage, + transfer_filter::PreLpTransfer, }; use sp_runtime::traits::One; use crate::{ ForeignInvestments, Investments, LiquidityPools, LiquidityPoolsAxelarGateway, LiquidityPoolsGateway, LocationToAccountId, OrderBook, OrmlAssetRegistry, Permissions, - PoolSystem, Runtime, RuntimeEvent, RuntimeOrigin, Timestamp, Tokens, TreasuryAccount, + PoolSystem, Runtime, RuntimeEvent, RuntimeOrigin, Timestamp, Tokens, TransferAllowList, + TreasuryAccount, }; parameter_types! { @@ -78,6 +80,7 @@ impl pallet_liquidity_pools::Config for Runtime { type Permission = Permissions; type PoolId = PoolId; type PoolInspect = PoolSystem; + type PreTransferFilter = PreLpTransfer; type RuntimeEvent = RuntimeEvent; type Time = Timestamp; type Tokens = Tokens; diff --git a/runtime/development/src/xcm.rs b/runtime/development/src/xcm.rs index cb64c53b52..42b0e0eff3 100644 --- a/runtime/development/src/xcm.rs +++ b/runtime/development/src/xcm.rs @@ -33,6 +33,7 @@ use orml_xcm_support::MultiNativeAsset; use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use runtime_common::{ + transfer_filter::PreXcmTransfer, xcm::{general_key, AccountIdToMultiLocation, FixedConversionRateProvider, LpInstanceRelayer}, xcm_fees::native_per_second, }; @@ -415,6 +416,10 @@ impl orml_xtokens::Config for Runtime { type XcmExecutor = XcmExecutor; } +impl pallet_restricted_xtokens::Config for Runtime { + type PreTransfer = PreXcmTransfer; +} + impl cumulus_pallet_xcm::Config for Runtime { type RuntimeEvent = RuntimeEvent; type XcmExecutor = XcmExecutor; diff --git a/runtime/integration-tests/Cargo.toml b/runtime/integration-tests/Cargo.toml index 3094845c8e..abbb81db2a 100644 --- a/runtime/integration-tests/Cargo.toml +++ b/runtime/integration-tests/Cargo.toml @@ -16,6 +16,7 @@ fudge = { workspace = true } fudge-core = { workspace = true } getrandom = { workspace = true } hex = { workspace = true } +hex-literal = { workspace = true } lazy_static = { workspace = true } serde = { workspace = true } thiserror = { workspace = true } @@ -136,6 +137,7 @@ pallet-pool-system = { workspace = true, features = ["std"] } pallet-preimage = { workspace = true, features = ["std"] } pallet-proxy = { workspace = true, features = ["std"] } pallet-restricted-tokens = { workspace = true, features = ["std"] } +pallet-restricted-xtokens = { workspace = true, features = ["std"] } pallet-rewards = { workspace = true, features = ["std"] } pallet-scheduler = { workspace = true, features = ["std"] } pallet-session = { workspace = true, features = ["std"] } diff --git a/runtime/integration-tests/src/generic/cases/example.rs b/runtime/integration-tests/src/generic/cases/example.rs index 730729412d..0e99907f03 100644 --- a/runtime/integration-tests/src/generic/cases/example.rs +++ b/runtime/integration-tests/src/generic/cases/example.rs @@ -23,7 +23,7 @@ fn transfer_balance() { // Set up all GenesisConfig for your initial state // You can choose `RuntimeEnv` by `FudgeEnv` to make it working with fudge // environment. - let mut env = RuntimeEnv::::from_storage( + let mut env = RuntimeEnv::::from_parachain_storage( Genesis::default() .add(pallet_balances::GenesisConfig:: { balances: vec![( @@ -32,7 +32,6 @@ fn transfer_balance() { )], }) .storage(), - Genesis::::default().storage(), ); // Call an extrinsic that would be processed immediately @@ -75,7 +74,7 @@ fn fudge_transfer_balance() { const TRANSFER: Balance = 1000 * CFG; const FOR_FEES: Balance = 1 * CFG; - let mut env = FudgeEnv::::from_storage( + let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() .add(pallet_balances::GenesisConfig:: { balances: vec![( @@ -84,7 +83,6 @@ fn fudge_transfer_balance() { )], }) .storage(), - Genesis::::default().storage(), ); env.submit_later( @@ -131,7 +129,7 @@ fn fudge_transfer_balance() { } fn call_api() { - let env = RuntimeEnv::::from_storage(Default::default(), Default::default()); + let env = RuntimeEnv::::default(); env.parachain_state(|| { // If imported the trait: sp_api::runtime_decl_for_core::CoreV4, @@ -144,7 +142,7 @@ fn call_api() { } fn fudge_call_api() { - let env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let env = FudgeEnv::::default(); // Exclusive from fudge environment. // It uses a client to access the runtime api. @@ -160,7 +158,7 @@ fn fudge_call_api() { } fn pass_time_one_block() { - let mut env = RuntimeEnv::::from_storage(Default::default(), Default::default()); + let mut env = RuntimeEnv::::default(); let before = env.parachain_state(|| pallet_timestamp::Pallet::::get()); diff --git a/runtime/integration-tests/src/generic/cases/investments.rs b/runtime/integration-tests/src/generic/cases/investments.rs index dd559a9517..8fb446dd41 100644 --- a/runtime/integration-tests/src/generic/cases/investments.rs +++ b/runtime/integration-tests/src/generic/cases/investments.rs @@ -38,13 +38,12 @@ mod common { use super::*; pub fn initialize_state_for_investments, T: Runtime>() -> E { - let mut env = E::from_storage( + let mut env = E::from_parachain_storage( Genesis::::default() .add(genesis::balances(T::ExistentialDeposit::get() + FOR_FEES)) .add(genesis::assets(vec![Usd6::ID])) .add(genesis::tokens(vec![(Usd6::ID, Usd6::ED)])) .storage(), - Genesis::::default().storage(), ); env.parachain_state_mut(|| { diff --git a/runtime/integration-tests/src/generic/cases/liquidity_pools.rs b/runtime/integration-tests/src/generic/cases/liquidity_pools.rs index 37ea310a51..b6832c6364 100644 --- a/runtime/integration-tests/src/generic/cases/liquidity_pools.rs +++ b/runtime/integration-tests/src/generic/cases/liquidity_pools.rs @@ -106,6 +106,41 @@ mod utils { env.pass(Blocks::ByNumber(1)); } + + pub fn setup_usdc_xcm(env: &mut FudgeEnv) { + env.parachain_state_mut(|| { + // Set the XCM version used when sending XCM messages to USDC parachain. + assert_ok!(pallet_xcm::Pallet::::force_xcm_version( + ::RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 1, + Junctions::X1(Junction::Parachain(1000)), + )), + XCM_VERSION, + )); + }); + + env.relay_state_mut(|| { + assert_ok!(polkadot_runtime_parachains::hrmp::Pallet::< + FudgeRelayRuntime, + >::force_open_hrmp_channel( + as frame_system::Config>::RuntimeOrigin::root(), + Id::from(T::FudgeHandle::PARA_ID), + Id::from(1000), + 10, + 1024, + )); + + assert_ok!(polkadot_runtime_parachains::hrmp::Pallet::< + FudgeRelayRuntime, + >::force_process_hrmp_open( + as frame_system::Config>::RuntimeOrigin::root(), + 0, + )); + }); + + env.pass(Blocks::ByNumber(1)); + } } type FudgeRelayRuntime = <::FudgeHandle as FudgeHandle>::RelayRuntime; @@ -371,11 +406,10 @@ mod altair { } fn test_air_transfers_to_and_from_sibling() { - let mut env = FudgeEnv::::from_storage( + let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() .add(genesis::balances::(air(10))) .storage(), - Default::default(), ); setup_xcm(&mut env); @@ -450,7 +484,7 @@ mod altair { } fn transfer_ausd_to_altair() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); setup_xcm(&mut env); @@ -609,7 +643,7 @@ mod altair { } fn transfer_ksm_to_and_from_relay_chain() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let transfer_amount: Balance = ksm(2); let currency_id = CurrencyId::ForeignAsset(3001); @@ -673,11 +707,10 @@ mod altair { } fn transfer_foreign_sibling_to_altair() { - let mut env = FudgeEnv::::from_storage( + let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() .add(genesis::balances::(air(10))) .storage(), - Default::default(), ); setup_xcm(&mut env); @@ -782,6 +815,7 @@ mod altair { fn transfer_wormhole_usdc_karura_to_altair() { let mut env = FudgeEnv::::from_storage( + Default::default(), Default::default(), Genesis::default() .add(genesis::balances::(air(10))) @@ -892,7 +926,7 @@ mod altair { use super::*; fn register_air_works() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { let meta: AssetMetadata = AssetMetadata { @@ -919,7 +953,7 @@ mod altair { } fn register_foreign_asset_works() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { let meta: AssetMetadata = AssetMetadata { @@ -950,7 +984,7 @@ mod altair { // Verify that registering tranche tokens is not allowed through extrinsics fn register_tranche_asset_blocked() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { let meta: AssetMetadata = AssetMetadata { @@ -991,7 +1025,7 @@ mod altair { use super::*; fn convert_air() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); assert_eq!(parachains::kusama::altair::AIR_KEY.to_vec(), vec![0, 1]); @@ -1027,7 +1061,7 @@ mod altair { /// Verify that Tranche tokens are not handled by the CurrencyIdConvert /// since we don't allow Tranche tokens to be transferable through XCM. fn convert_tranche() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let tranche_currency = CurrencyId::Tranche(401, [0; 16]); let tranche_id = @@ -1060,7 +1094,7 @@ mod altair { } fn convert_ausd() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { assert_eq!(parachains::kusama::karura::AUSD_KEY, &[0, 129]); @@ -1088,7 +1122,7 @@ mod altair { } fn convert_ksm() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let ksm_location: MultiLocation = MultiLocation::parent().into(); @@ -1108,7 +1142,7 @@ mod altair { } fn convert_unkown_multilocation() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let unknown_location: MultiLocation = MultiLocation::new( 1, @@ -1121,7 +1155,7 @@ mod altair { } fn convert_unsupported_currency() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { assert_eq!( @@ -1145,21 +1179,28 @@ mod altair { mod centrifuge { use centrifuge_runtime::CurrencyIdConvert; + use sp_core::Get; use super::*; mod utils { + use xcm::v3::NetworkId; + use super::*; /// The test asset id attributed to DOT pub const DOT_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(91); + pub const LP_ETH_USDC: CurrencyId = CurrencyId::ForeignAsset(100_001); + + pub const USDC: CurrencyId = CurrencyId::ForeignAsset(6); + /// An Asset that is NOT XCM transferable pub const NO_XCM_ASSET_ID: CurrencyId = CurrencyId::ForeignAsset(401); /// Register DOT in the asset registry. /// It should be executed within an externalities environment. - pub fn register_dot() { + pub fn register_dot() { let meta: AssetMetadata = AssetMetadata { decimals: 10, name: "Polkadot".into(), @@ -1178,6 +1219,62 @@ mod centrifuge { )); } + pub fn register_lp_eth_usdc() { + let meta: AssetMetadata = AssetMetadata { + decimals: 6, + name: "LP Ethereum Wrapped USDC".into(), + symbol: "LpEthUSDC".into(), + existential_deposit: 1_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 0, + X3( + PalletInstance(103), + GlobalConsensus(NetworkId::Ethereum { chain_id: 1 }), + AccountKey20 { + network: None, + key: hex_literal::hex!("a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"), + }, + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::LiquidityPools, + ..CustomMetadata::default() + }, + }; + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(LP_ETH_USDC) + )); + } + + pub fn register_usdc() { + let meta: AssetMetadata = AssetMetadata { + decimals: 6, + name: "USD Circle".into(), + symbol: "USDC".into(), + existential_deposit: 1_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X3( + Junction::Parachain(1000), + Junction::PalletInstance(50), + Junction::GeneralIndex(1337), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(USDC) + )); + } + /// Register AUSD in the asset registry. /// It should be executed within an externalities environment. pub fn register_ausd() { @@ -1307,6 +1404,14 @@ mod centrifuge { fee(10) } + pub fn lp_eth_usdc_fee() -> Balance { + fee(6) + } + + pub fn usdc_fee() -> Balance { + fee(6) + } + pub fn calc_fee(fee_per_second: Balance) -> Balance { // We divide the fee to align its unit and multiply by 4 as that seems to be the // unit of time the tests take. @@ -1331,9 +1436,83 @@ mod centrifuge { amount * dollar(10) } + pub fn lp_eth_usdc(amount: Balance) -> Balance { + amount * dollar(6) + } + + pub fn usdc(amount: Balance) -> Balance { + amount * dollar(6) + } + pub fn foreign(amount: Balance, decimals: u32) -> Balance { amount * dollar(decimals) } + + pub fn transfer_dot_from_relay_chain(env: &mut FudgeEnv) { + let alice_initial_dot = dot(10); + let transfer_amount: Balance = dot(3); + + env.parachain_state_mut(|| { + register_dot::(); + assert_eq!( + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), + 0 + ); + }); + + env.relay_state_mut(|| { + assert_ok!( + pallet_balances::Pallet::>::force_set_balance( + as frame_system::Config>::RuntimeOrigin::root(), + Keyring::Alice.to_account_id().into(), + alice_initial_dot, + ) + ); + + assert_ok!( + pallet_xcm::Pallet::>::force_xcm_version( + as frame_system::Config>::RuntimeOrigin::root(), + Box::new(MultiLocation::new( + 0, + Junctions::X1(Junction::Parachain(T::FudgeHandle::PARA_ID)), + )), + XCM_VERSION, + ) + ); + + assert_ok!( + pallet_xcm::Pallet::>::reserve_transfer_assets( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Box::new(Parachain(T::FudgeHandle::PARA_ID).into()), + Box::new( + Junction::AccountId32 { + network: None, + id: Keyring::Alice.into(), + } + .into() + ), + Box::new((Here, transfer_amount).into()), + 0 + ) + ); + + assert_eq!( + pallet_balances::Pallet::>::free_balance( + &Keyring::Alice.into() + ), + alice_initial_dot - transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.parachain_state(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), + transfer_amount - dot_fee() + ); + }); + } } use utils::*; @@ -1342,7 +1521,7 @@ mod centrifuge { use super::*; fn register_cfg_works() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { let meta: AssetMetadata = AssetMetadata { @@ -1369,7 +1548,7 @@ mod centrifuge { } fn register_foreign_asset_works() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { let meta: AssetMetadata = AssetMetadata { @@ -1400,7 +1579,7 @@ mod centrifuge { // Verify that registering tranche tokens is not allowed through extrinsics fn register_tranche_asset_blocked() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { let meta: AssetMetadata = AssetMetadata { @@ -1441,7 +1620,7 @@ mod centrifuge { use super::*; fn convert_cfg() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); assert_eq!(parachains::polkadot::centrifuge::CFG_KEY, &[0, 1]); @@ -1479,7 +1658,7 @@ mod centrifuge { /// v2 MultiLocation, that `CurrencyIdConvert` can look it up given an /// identical location in XCM v3. fn convert_cfg_xcm_v2() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); assert_eq!(parachains::polkadot::centrifuge::CFG_KEY, &[0, 1]); @@ -1517,7 +1696,7 @@ mod centrifuge { /// Verify that a registered token that is NOT XCM transferable is /// filtered out by CurrencyIdConvert as expected. fn convert_no_xcm_token() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { register_no_xcm_token::(); @@ -1530,7 +1709,7 @@ mod centrifuge { } fn convert_ausd() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); assert_eq!(parachains::polkadot::acala::AUSD_KEY, &[0, 1]); @@ -1558,7 +1737,7 @@ mod centrifuge { } fn convert_dot() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let dot_location: MultiLocation = MultiLocation::parent(); @@ -1578,7 +1757,7 @@ mod centrifuge { } fn convert_unknown_multilocation() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let unknown_location: MultiLocation = MultiLocation::new( 1, @@ -1594,7 +1773,7 @@ mod centrifuge { } fn convert_unsupported_currency() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { assert_eq!( @@ -1616,104 +1795,706 @@ mod centrifuge { crate::test_for_runtimes!([centrifuge], convert_unsupported_currency); } - mod transfers { + mod restricted_transfers { + use cfg_types::{ + domain_address::{Domain, DomainAddress}, + locations::Location, + }; + use frame_support::{pallet_prelude::GenesisBuild, traits::fungibles::Mutate, BoundedVec}; + use liquidity_pools_gateway_routers::{ + DomainRouter, EthereumXCMRouter, XCMRouter, XcmDomain, + }; + use polkadot_parachain::primitives::ValidationCode; + use polkadot_runtime_parachains::{ + paras, + paras::{ParaGenesisArgs, ParaKind}, + }; + use sp_core::{Hasher, H160}; + use sp_runtime::traits::BlakeTwo256; + use super::*; + use crate::generic::envs::runtime_env::RuntimeEnv; - fn transfer_cfg_to_sibling(env: &mut FudgeEnv) { - let alice_initial_balance = cfg(10); - let transfer_amount = cfg(5); - let cfg_in_sibling = CurrencyId::ForeignAsset(12); + const TRANSFER_AMOUNT: u128 = 10; - // CFG Metadata - let meta: AssetMetadata = AssetMetadata { - decimals: 18, - name: "Centrifuge".into(), - symbol: "CFG".into(), - existential_deposit: 1_000_000_000_000, - location: Some(VersionedMultiLocation::V3(MultiLocation::new( - 1, - X2( - Parachain(T::FudgeHandle::PARA_ID), - general_key(parachains::polkadot::centrifuge::CFG_KEY), - ), - ))), - additional: CustomMetadata { - transferability: CrossChainTransferability::Xcm(Default::default()), - ..CustomMetadata::default() - }, - }; + fn xcm_location() -> MultiLocation { + MultiLocation::new( + 1, + X1(AccountId32 { + id: Keyring::Alice.into(), + network: None, + }), + ) + } + + fn allowed_xcm_location() -> Location { + Location::XCM(BlakeTwo256::hash(&xcm_location().encode())) + } + + fn add_allowance(account: Keyring, asset: CurrencyId, location: Location) { + assert_ok!( + pallet_transfer_allowlist::Pallet::::add_transfer_allowance( + RawOrigin::Signed(account.into()).into(), + asset, + location + ) + ); + } + + fn restrict_lp_eth_usdc_transfer() { + let mut env = RuntimeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .add(orml_tokens::GenesisConfig:: { + balances: vec![( + Keyring::Alice.to_account_id(), + LP_ETH_USDC, + T::ExistentialDeposit::get() + lp_eth_usdc(TRANSFER_AMOUNT), + )], + }) + .storage(), + ); env.parachain_state_mut(|| { - assert_eq!( - pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), - alice_initial_balance + register_lp_eth_usdc::(); + + let pre_transfer_alice = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Alice.to_account_id(), ); - assert_eq!( - pallet_balances::Pallet::::free_balance(¶chain_account( - T::FudgeHandle::SIBLING_ID - )), - 0 + let pre_transfer_bob = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Bob.to_account_id(), + ); + let pre_transfer_charlie = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Charlie.to_account_id(), ); - assert_ok!(orml_asset_registry::Pallet::::register_asset( - ::RuntimeOrigin::root(), - meta.clone(), - Some(CurrencyId::Native), - )); - }); + add_allowance::( + Keyring::Alice, + LP_ETH_USDC, + Location::Local(Keyring::Bob.to_account_id()), + ); - env.sibling_state_mut(|| { - assert_eq!( - orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()), - 0 + assert_noop!( + pallet_restricted_tokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Keyring::Charlie.into(), + LP_ETH_USDC, + lp_eth_usdc(TRANSFER_AMOUNT) + ), + pallet_restricted_tokens::Error::::PreConditionsNotMet ); - assert_ok!(orml_asset_registry::Pallet::::register_asset( - ::RuntimeOrigin::root(), - meta, - Some(cfg_in_sibling) - )); - }); + let after_transfer_alice = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Alice.to_account_id(), + ); + let after_transfer_charlie = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Charlie.to_account_id(), + ); - env.parachain_state_mut(|| { - assert_ok!(orml_xtokens::Pallet::::transfer( + assert_eq!(after_transfer_alice, pre_transfer_alice); + assert_eq!(after_transfer_charlie, pre_transfer_charlie); + + assert_ok!(pallet_restricted_tokens::Pallet::::transfer( RawOrigin::Signed(Keyring::Alice.into()).into(), - CurrencyId::Native, - transfer_amount, - Box::new( - MultiLocation::new( - 1, - X2( - Parachain(T::FudgeHandle::SIBLING_ID), - Junction::AccountId32 { - network: None, - id: Keyring::Bob.into(), - } - ) - ) - .into() - ), - WeightLimit::Limited(8_000_000_000_000.into()), - )); + Keyring::Bob.into(), + LP_ETH_USDC, + lp_eth_usdc(TRANSFER_AMOUNT) + ),); - // Confirm that Alice's balance is initial balance - amount transferred - assert_eq!( - pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), - alice_initial_balance - transfer_amount + let after_transfer_alice = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Alice.to_account_id(), + ); + let after_transfer_bob = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Bob.to_account_id(), + ); + let after_transfer_charlie = orml_tokens::Pallet::::free_balance( + LP_ETH_USDC, + &Keyring::Charlie.to_account_id(), ); - // Verify that the amount transferred is now part of the sibling account here assert_eq!( - pallet_balances::Pallet::::free_balance(¶chain_account( - T::FudgeHandle::SIBLING_ID - )), - transfer_amount + after_transfer_alice, + pre_transfer_alice - lp_eth_usdc(TRANSFER_AMOUNT) + ); + assert_eq!( + after_transfer_bob, + pre_transfer_bob + lp_eth_usdc(TRANSFER_AMOUNT) ); + assert_eq!(after_transfer_charlie, pre_transfer_charlie); }); + } - env.pass(Blocks::ByNumber(1)); + fn restrict_lp_eth_usdc_lp_transfer() { + let mut env = FudgeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .add(orml_tokens::GenesisConfig:: { + balances: vec![( + Keyring::Alice.to_account_id(), + LP_ETH_USDC, + T::ExistentialDeposit::get() + lp_eth_usdc(TRANSFER_AMOUNT), + )], + }) + .storage(), + ); - env.sibling_state_mut(|| { + setup_xcm(&mut env); + + env.parachain_state_mut(|| { + register_usdc::(); + register_lp_eth_usdc::(); + + assert_ok!(orml_tokens::Pallet::::set_balance( + ::RuntimeOrigin::root(), + ::Sender::get().into(), + USDC, + usdc(1_000), + 0, + )); + + let router = DomainRouter::EthereumXCM(EthereumXCMRouter:: { + router: XCMRouter { + xcm_domain: XcmDomain { + location: Box::new( + MultiLocation::new(1, X1(Parachain(T::FudgeHandle::SIBLING_ID))) + .into(), + ), + ethereum_xcm_transact_call_index: BoundedVec::truncate_from(vec![ + 38, 0, + ]), + contract_address: H160::from_low_u64_be(11), + max_gas_limit: 700_000, + transact_required_weight_at_most: Default::default(), + overall_weight: Default::default(), + fee_currency: USDC, + fee_amount: usdc(1), + }, + _marker: Default::default(), + }, + _marker: Default::default(), + }); + + assert_ok!( + pallet_liquidity_pools_gateway::Pallet::::set_domain_router( + ::RuntimeOrigin::root(), + Domain::EVM(1), + router, + ) + ); + + let receiver = H160::from_slice( + &>::as_ref( + &Keyring::Charlie.to_account_id(), + )[0..20], + ); + + let domain_address = DomainAddress::EVM(1, receiver.into()); + + add_allowance::( + Keyring::Alice, + LP_ETH_USDC, + Location::Address(domain_address.clone()), + ); + + assert_noop!( + pallet_liquidity_pools::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + LP_ETH_USDC, + DomainAddress::EVM(1, [1u8; 20]), + lp_eth_usdc(TRANSFER_AMOUNT), + ), + pallet_transfer_allowlist::Error::::NoAllowanceForDestination + ); + + assert_ok!(pallet_liquidity_pools::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + LP_ETH_USDC, + domain_address, + lp_eth_usdc(TRANSFER_AMOUNT), + )); + + let domain_acc = Domain::convert(Domain::EVM(1)); + + assert_eq!( + orml_tokens::Pallet::::free_balance(LP_ETH_USDC, &domain_acc), + lp_eth_usdc(TRANSFER_AMOUNT), + ); + }); + } + + fn restrict_usdc_transfer() { + let mut env = RuntimeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .add(orml_tokens::GenesisConfig:: { + balances: vec![( + Keyring::Alice.to_account_id(), + USDC, + T::ExistentialDeposit::get() + usdc(TRANSFER_AMOUNT), + )], + }) + .storage(), + ); + + env.parachain_state_mut(|| { + register_usdc::(); + + let pre_transfer_alice = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Alice.to_account_id()); + let pre_transfer_bob = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Bob.to_account_id()); + let pre_transfer_charlie = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Charlie.to_account_id()); + + add_allowance::( + Keyring::Alice, + USDC, + Location::Local(Keyring::Bob.to_account_id()), + ); + + assert_noop!( + pallet_restricted_tokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Keyring::Charlie.into(), + USDC, + lp_eth_usdc(TRANSFER_AMOUNT) + ), + pallet_restricted_tokens::Error::::PreConditionsNotMet + ); + + let after_transfer_alice = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Alice.to_account_id()); + let after_transfer_charlie = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Charlie.to_account_id()); + + assert_eq!(after_transfer_alice, pre_transfer_alice); + assert_eq!(after_transfer_charlie, pre_transfer_charlie); + + assert_ok!(pallet_restricted_tokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Keyring::Bob.into(), + USDC, + usdc(TRANSFER_AMOUNT) + ),); + + let after_transfer_alice = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Alice.to_account_id()); + let after_transfer_bob = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Bob.to_account_id()); + let after_transfer_charlie = + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Charlie.to_account_id()); + + assert_eq!( + after_transfer_alice, + pre_transfer_alice - usdc(TRANSFER_AMOUNT) + ); + assert_eq!(after_transfer_bob, pre_transfer_bob + usdc(TRANSFER_AMOUNT)); + assert_eq!(after_transfer_charlie, pre_transfer_charlie); + }); + } + + fn restrict_usdc_xcm_transfer() { + let mut env = FudgeEnv::::from_storage( + >>::build_storage( + ¶s::GenesisConfig { + paras: vec![( + 1000.into(), + ParaGenesisArgs { + genesis_head: Default::default(), + validation_code: ValidationCode::from(vec![0, 1, 2, 3]), + para_kind: ParaKind::Parachain, + }, + )], + }, + ) + .unwrap(), + Genesis::default() + .add(genesis::balances::(cfg(10))) + .storage(), + Default::default(), + ); + + setup_xcm(&mut env); + + setup_usdc_xcm(&mut env); + + env.sibling_state_mut(|| { + register_usdc::(); + }); + + env.parachain_state_mut(|| { + register_usdc::(); + + let alice_initial_usdc = usdc(3_000); + + assert_ok!(orml_tokens::Pallet::::mint_into( + USDC, + &Keyring::Alice.into(), + alice_initial_usdc + )); + + assert_ok!( + pallet_transfer_allowlist::Pallet::::add_transfer_allowance( + RawOrigin::Signed(Keyring::Alice.into()).into(), + USDC, + Location::XCM(BlakeTwo256::hash( + &MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + id: Keyring::Alice.into(), + network: None, + } + ) + ) + .encode() + )) + ) + ); + + assert_noop!( + pallet_restricted_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + USDC, + usdc(1_000), + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + id: Keyring::Bob.into(), + network: None, + } + ) + ) + .into() + ), + WeightLimit::Unlimited, + ), + pallet_transfer_allowlist::Error::::NoAllowanceForDestination + ); + + assert_ok!(pallet_restricted_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + USDC, + usdc(1_000), + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + id: Keyring::Alice.into(), + network: None, + } + ) + ) + .into() + ), + WeightLimit::Unlimited, + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance(USDC, &Keyring::Alice.into()), + alice_initial_usdc - usdc(1_000), + ); + }); + + // NOTE - we cannot confirm that the Alice account present on the + // sibling receives this transfer since the orml_xtokens pallet + // sends a message to parachain 1000 (the parachain of the USDC + // currency) which in turn should send a message to the sibling. + // Since parachain 1000 is just a dummy added in the paras + // genesis config and not an actual sibling with a runtime, the + // transfer does not take place. + } + + fn restrict_dot_transfer() { + let mut env = RuntimeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .add(orml_tokens::GenesisConfig:: { + balances: vec![( + Keyring::Alice.to_account_id(), + DOT_ASSET_ID, + T::ExistentialDeposit::get() + dot(TRANSFER_AMOUNT), + )], + }) + .storage(), + ); + + env.parachain_state_mut(|| { + register_dot::(); + + let pre_transfer_alice = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Alice.to_account_id(), + ); + let pre_transfer_bob = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Bob.to_account_id(), + ); + let pre_transfer_charlie = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Charlie.to_account_id(), + ); + + add_allowance::( + Keyring::Alice, + DOT_ASSET_ID, + Location::Local(Keyring::Bob.to_account_id()), + ); + + assert_noop!( + pallet_restricted_tokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Keyring::Charlie.into(), + DOT_ASSET_ID, + dot(TRANSFER_AMOUNT) + ), + pallet_restricted_tokens::Error::::PreConditionsNotMet + ); + + let after_transfer_alice = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Alice.to_account_id(), + ); + let after_transfer_charlie = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Charlie.to_account_id(), + ); + + assert_eq!(after_transfer_alice, pre_transfer_alice); + assert_eq!(after_transfer_charlie, pre_transfer_charlie); + + assert_ok!(pallet_restricted_tokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + Keyring::Bob.into(), + DOT_ASSET_ID, + dot(TRANSFER_AMOUNT) + ),); + + let after_transfer_alice = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Alice.to_account_id(), + ); + let after_transfer_bob = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Bob.to_account_id(), + ); + let after_transfer_charlie = orml_tokens::Pallet::::free_balance( + DOT_ASSET_ID, + &Keyring::Charlie.to_account_id(), + ); + + assert_eq!( + after_transfer_alice, + pre_transfer_alice - dot(TRANSFER_AMOUNT) + ); + assert_eq!(after_transfer_bob, pre_transfer_bob + dot(TRANSFER_AMOUNT)); + assert_eq!(after_transfer_charlie, pre_transfer_charlie); + }); + } + + fn restrict_dot_xcm_transfer() { + let mut env = FudgeEnv::::from_parachain_storage( + Genesis::default() + .add(genesis::balances::(cfg(10))) + .storage(), + ); + + transfer_dot_from_relay_chain(&mut env); + + env.parachain_state_mut(|| { + let alice_initial_dot = + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()); + + assert_eq!(alice_initial_dot, dot(3) - dot_fee()); + + assert_ok!(pallet_xcm::Pallet::::force_xcm_version( + ::RuntimeOrigin::root(), + Box::new(MultiLocation::new(1, Junctions::Here)), + XCM_VERSION, + )); + + assert_ok!( + pallet_transfer_allowlist::Pallet::::add_transfer_allowance( + RawOrigin::Signed(Keyring::Alice.into()).into(), + DOT_ASSET_ID, + allowed_xcm_location() + ) + ); + + assert_noop!( + pallet_restricted_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + DOT_ASSET_ID, + dot(1), + Box::new( + MultiLocation::new( + 1, + X1(Junction::AccountId32 { + id: Keyring::Bob.into(), + network: None, + }) + ) + .into() + ), + WeightLimit::Unlimited, + ), + pallet_transfer_allowlist::Error::::NoAllowanceForDestination + ); + + assert_ok!(pallet_restricted_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + DOT_ASSET_ID, + dot(1), + Box::new( + MultiLocation::new( + 1, + X1(Junction::AccountId32 { + id: Keyring::Alice.into(), + network: None, + }) + ) + .into() + ), + WeightLimit::Unlimited, + )); + + assert_eq!( + orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), + alice_initial_dot - dot(1), + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.relay_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::>::free_balance( + &Keyring::Alice.into() + ), + 79628418552 + ); + }); + } + + crate::test_for_runtimes!([centrifuge], restrict_lp_eth_usdc_transfer); + crate::test_for_runtimes!([centrifuge], restrict_lp_eth_usdc_lp_transfer); + crate::test_for_runtimes!([centrifuge], restrict_usdc_transfer); + crate::test_for_runtimes!([centrifuge], restrict_usdc_xcm_transfer); + crate::test_for_runtimes!([centrifuge], restrict_dot_transfer); + crate::test_for_runtimes!([centrifuge], restrict_dot_xcm_transfer); + } + + mod transfers { + use super::*; + + fn transfer_cfg_to_sibling(env: &mut FudgeEnv) { + let alice_initial_balance = cfg(10); + let transfer_amount = cfg(5); + let cfg_in_sibling = CurrencyId::ForeignAsset(12); + + // CFG Metadata + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Centrifuge".into(), + symbol: "CFG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::PARA_ID), + general_key(parachains::polkadot::centrifuge::CFG_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + env.parachain_state_mut(|| { + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance + ); + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::SIBLING_ID + )), + 0 + ); + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta.clone(), + Some(CurrencyId::Native), + )); + }); + + env.sibling_state_mut(|| { + assert_eq!( + orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()), + 0 + ); + + assert_ok!(orml_asset_registry::Pallet::::register_asset( + ::RuntimeOrigin::root(), + meta, + Some(cfg_in_sibling) + )); + }); + + env.parachain_state_mut(|| { + assert_ok!(orml_xtokens::Pallet::::transfer( + RawOrigin::Signed(Keyring::Alice.into()).into(), + CurrencyId::Native, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(T::FudgeHandle::SIBLING_ID), + Junction::AccountId32 { + network: None, + id: Keyring::Bob.into(), + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + pallet_balances::Pallet::::free_balance(&Keyring::Alice.into()), + alice_initial_balance - transfer_amount + ); + + // Verify that the amount transferred is now part of the sibling account here + assert_eq!( + pallet_balances::Pallet::::free_balance(¶chain_account( + T::FudgeHandle::SIBLING_ID + )), + transfer_amount + ); + }); + + env.pass(Blocks::ByNumber(1)); + + env.sibling_state_mut(|| { let current_balance = orml_tokens::Pallet::::free_balance(cfg_in_sibling, &Keyring::Bob.into()); @@ -1726,11 +2507,10 @@ mod centrifuge { } fn test_cfg_transfers_to_and_from_sibling() { - let mut env = FudgeEnv::::from_storage( + let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() .add(genesis::balances::(cfg(10))) .storage(), - Default::default(), ); setup_xcm(&mut env); @@ -1806,7 +2586,7 @@ mod centrifuge { } fn transfer_ausd_to_centrifuge() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); setup_xcm(&mut env); @@ -1898,74 +2678,8 @@ mod centrifuge { }); } - fn transfer_dot_from_relay_chain(env: &mut FudgeEnv) { - let alice_initial_dot = dot(10); - let transfer_amount: Balance = dot(3); - - env.parachain_state_mut(|| { - register_dot::(); - assert_eq!( - orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), - 0 - ); - }); - - env.relay_state_mut(|| { - assert_ok!( - pallet_balances::Pallet::>::force_set_balance( - as frame_system::Config>::RuntimeOrigin::root(), - Keyring::Alice.to_account_id().into(), - alice_initial_dot, - ) - ); - - assert_ok!( - pallet_xcm::Pallet::>::force_xcm_version( - as frame_system::Config>::RuntimeOrigin::root(), - Box::new(MultiLocation::new( - 0, - Junctions::X1(Junction::Parachain(T::FudgeHandle::PARA_ID)), - )), - XCM_VERSION, - ) - ); - - assert_ok!( - pallet_xcm::Pallet::>::reserve_transfer_assets( - RawOrigin::Signed(Keyring::Alice.into()).into(), - Box::new(Parachain(T::FudgeHandle::PARA_ID).into()), - Box::new( - Junction::AccountId32 { - network: None, - id: Keyring::Alice.into(), - } - .into() - ), - Box::new((Here, transfer_amount).into()), - 0 - ) - ); - - assert_eq!( - pallet_balances::Pallet::>::free_balance( - &Keyring::Alice.into() - ), - alice_initial_dot - transfer_amount - ); - }); - - env.pass(Blocks::ByNumber(1)); - - env.parachain_state(|| { - assert_eq!( - orml_tokens::Pallet::::free_balance(DOT_ASSET_ID, &Keyring::Alice.into()), - transfer_amount - dot_fee() - ); - }); - } - fn transfer_dot_to_and_from_relay_chain() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); transfer_dot_from_relay_chain(&mut env); @@ -2017,11 +2731,10 @@ mod centrifuge { } fn transfer_foreign_sibling_to_centrifuge() { - let mut env = FudgeEnv::::from_storage( + let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() .add(genesis::balances::(cfg(10))) .storage(), - Default::default(), ); setup_xcm(&mut env); @@ -2127,6 +2840,7 @@ mod centrifuge { fn transfer_wormhole_usdc_acala_to_centrifuge() { let mut env = FudgeEnv::::from_storage( + Default::default(), Default::default(), Genesis::default() .add(genesis::balances::(cfg(10))) @@ -2239,7 +2953,7 @@ mod all { use super::*; fn xtokens_transfer() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); env.parachain_state_mut(|| { assert_noop!( @@ -2268,7 +2982,7 @@ mod all { } fn xtokens_transfer_multiasset() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let tranche_currency = CurrencyId::Tranche(401, [0; 16]); let tranche_id = @@ -2315,7 +3029,7 @@ mod all { } fn xtokens_transfer_multiassets() { - let mut env = FudgeEnv::::from_storage(Default::default(), Default::default()); + let mut env = FudgeEnv::::default(); let tranche_currency = CurrencyId::Tranche(401, [0; 16]); let tranche_id = diff --git a/runtime/integration-tests/src/generic/cases/loans.rs b/runtime/integration-tests/src/generic/cases/loans.rs index 046de5552c..dbe0327ec7 100644 --- a/runtime/integration-tests/src/generic/cases/loans.rs +++ b/runtime/integration-tests/src/generic/cases/loans.rs @@ -66,13 +66,12 @@ mod common { use super::*; pub fn initialize_state_for_loans, T: Runtime>() -> E { - let mut env = E::from_storage( + let mut env = E::from_parachain_storage( Genesis::::default() .add(genesis::balances(T::ExistentialDeposit::get() + FOR_FEES)) .add(genesis::assets(vec![Usd6::ID])) .add(genesis::tokens(vec![(Usd6::ID, Usd6::ED)])) .storage(), - Genesis::::default().storage(), ); env.parachain_state_mut(|| { diff --git a/runtime/integration-tests/src/generic/config.rs b/runtime/integration-tests/src/generic/config.rs index 635b477931..974fd590e4 100644 --- a/runtime/integration-tests/src/generic/config.rs +++ b/runtime/integration-tests/src/generic/config.rs @@ -8,6 +8,7 @@ use cfg_traits::Millis; use cfg_types::{ fixed_point::{Quantity, Rate}, investments::InvestmentPortfolio, + locations::Location, oracles::OracleKey, permissions::{PermissionScope, Role}, tokens::{CurrencyId, CustomMetadata, TrancheCurrency}, @@ -15,10 +16,11 @@ use cfg_types::{ use codec::Codec; use fp_self_contained::{SelfContainedCall, UncheckedExtrinsic}; use frame_support::{ - dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo}, - traits::IsType, + dispatch::{DispatchInfo, GetDispatchInfo, PostDispatchInfo, RawOrigin}, + traits::{IsType, OriginTrait}, Parameter, }; +use liquidity_pools_gateway_routers::DomainRouter; use pallet_transaction_payment::CurrencyAdapter; use runtime_common::{ apis, @@ -48,6 +50,7 @@ pub trait Runtime: RuntimeEvent = Self::RuntimeEventExt, BlockNumber = BlockNumber, Lookup = AccountIdLookup, + RuntimeOrigin = Self::RuntimeOriginExt, > + pallet_pool_system::Config< CurrencyId = CurrencyId, Balance = Balance, @@ -97,6 +100,13 @@ pub trait Runtime: + orml_xtokens::Config + pallet_xcm::Config + pallet_restricted_tokens::Config + + pallet_restricted_xtokens::Config + + pallet_transfer_allowlist::Config + + pallet_liquidity_pools::Config + + pallet_liquidity_pools_gateway::Config> + + pallet_xcm_transactor::Config + + pallet_ethereum::Config + + pallet_ethereum_transaction::Config { /// Just the RuntimeCall type, but redefined with extra bounds. /// You can add `From` bounds in order to convert pallet calls to @@ -136,6 +146,13 @@ pub trait Runtime: + From> + From>; + type RuntimeOriginExt: Into, ::RuntimeOrigin>> + + From> + + Clone + + OriginTrait::RuntimeCall> + + From + + Into::RuntimeOrigin>>; + /// Block used by the runtime type Block: Block< Hash = H256, diff --git a/runtime/integration-tests/src/generic/env.rs b/runtime/integration-tests/src/generic/env.rs index 5118d6deca..ad2ebd2ff8 100644 --- a/runtime/integration-tests/src/generic/env.rs +++ b/runtime/integration-tests/src/generic/env.rs @@ -59,9 +59,16 @@ impl Blocks { } /// Define an environment behavior -pub trait Env { +pub trait Env: Default { + /// Load the environment from a parachain storage + fn from_parachain_storage(parachain_storage: Storage) -> Self; + /// Load the environment from a storage - fn from_storage(parachain_storage: Storage, sibling_storage: Storage) -> Self; + fn from_storage( + relay_storage: Storage, + parachain_storage: Storage, + sibling_storage: Storage, + ) -> Self; /// Submit an extrinsic mutating the state instantly and returning the /// consumed fee diff --git a/runtime/integration-tests/src/generic/envs/fudge_env.rs b/runtime/integration-tests/src/generic/envs/fudge_env.rs index 97a9294147..ea0f6663ef 100644 --- a/runtime/integration-tests/src/generic/envs/fudge_env.rs +++ b/runtime/integration-tests/src/generic/envs/fudge_env.rs @@ -30,10 +30,23 @@ pub struct FudgeEnv { nonce_storage: HashMap, } +impl Default for FudgeEnv { + fn default() -> Self { + Self::from_storage(Default::default(), Default::default(), Default::default()) + } +} + impl Env for FudgeEnv { - fn from_storage(parachain_storage: Storage, sibling_storage: Storage) -> Self { - let mut handle = - T::FudgeHandle::new(Storage::default(), parachain_storage, sibling_storage); + fn from_parachain_storage(parachain_storage: Storage) -> Self { + Self::from_storage(Default::default(), parachain_storage, Default::default()) + } + + fn from_storage( + relay_storage: Storage, + parachain_storage: Storage, + sibling_storage: Storage, + ) -> Self { + let mut handle = T::FudgeHandle::new(relay_storage, parachain_storage, sibling_storage); handle.evolve(); @@ -140,13 +153,12 @@ mod tests { use crate::generic::{env::Blocks, utils::genesis::Genesis}; fn correct_nonce_for_submit_later() { - let mut env = FudgeEnv::::from_storage( + let mut env = FudgeEnv::::from_parachain_storage( Genesis::default() .add(pallet_balances::GenesisConfig:: { balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], }) .storage(), - Genesis::::default().storage(), ); env.submit_later( diff --git a/runtime/integration-tests/src/generic/envs/runtime_env.rs b/runtime/integration-tests/src/generic/envs/runtime_env.rs index 8d7e7ca7f9..aa9188c644 100644 --- a/runtime/integration-tests/src/generic/envs/runtime_env.rs +++ b/runtime/integration-tests/src/generic/envs/runtime_env.rs @@ -1,6 +1,7 @@ use std::{cell::RefCell, marker::PhantomData, mem, rc::Rc}; use cfg_primitives::{AuraId, Balance, BlockNumber, Header}; +use cfg_types::ParaId; use codec::Encode; use cumulus_primitives_core::PersistedValidationData; use cumulus_primitives_parachain_inherent::ParachainInherentData; @@ -30,11 +31,26 @@ pub struct RuntimeEnv { parachain_ext: Rc>, sibling_ext: Rc>, pending_extrinsics: Vec<(Keyring, T::RuntimeCallExt)>, + pending_xcm: Vec<(ParaId, Vec)>, _config: PhantomData, } +impl Default for RuntimeEnv { + fn default() -> Self { + Self::from_storage(Default::default(), Default::default(), Default::default()) + } +} + impl Env for RuntimeEnv { - fn from_storage(mut parachain_storage: Storage, mut sibling_storage: Storage) -> Self { + fn from_parachain_storage(parachain_storage: Storage) -> Self { + Self::from_storage(Default::default(), parachain_storage, Default::default()) + } + + fn from_storage( + mut _relay_storage: Storage, + mut parachain_storage: Storage, + mut sibling_storage: Storage, + ) -> Self { // Needed for the aura usage pallet_aura::GenesisConfig:: { authorities: vec![AuraId::from(Public([0; 32]))], @@ -61,6 +77,7 @@ impl Env for RuntimeEnv { parachain_ext: Rc::new(RefCell::new(parachain_ext)), sibling_ext: Rc::new(RefCell::new(sibling_ext)), pending_extrinsics: Vec::default(), + pending_xcm: Vec::default(), _config: PhantomData, } } @@ -241,13 +258,12 @@ mod tests { use crate::generic::{env::Blocks, utils::genesis::Genesis}; fn correct_nonce_for_submit_now() { - let mut env = RuntimeEnv::::from_storage( + let mut env = RuntimeEnv::::from_parachain_storage( Genesis::default() .add(pallet_balances::GenesisConfig:: { balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], }) .storage(), - Genesis::::default().storage(), ); env.submit_now( @@ -264,13 +280,12 @@ mod tests { } fn correct_nonce_for_submit_later() { - let mut env = RuntimeEnv::::from_storage( + let mut env = RuntimeEnv::::from_parachain_storage( Genesis::default() .add(pallet_balances::GenesisConfig:: { balances: vec![(Keyring::Alice.to_account_id(), 1 * CFG)], }) .storage(), - Genesis::::default().storage(), ); env.submit_later( diff --git a/runtime/integration-tests/src/generic/impls.rs b/runtime/integration-tests/src/generic/impls.rs index 933b1a952e..6b79a21938 100644 --- a/runtime/integration-tests/src/generic/impls.rs +++ b/runtime/integration-tests/src/generic/impls.rs @@ -13,6 +13,7 @@ macro_rules! impl_runtime { type MaxTranchesExt = $runtime_path::MaxTranches; type RuntimeCallExt = $runtime_path::RuntimeCall; type RuntimeEventExt = $runtime_path::RuntimeEvent; + type RuntimeOriginExt = $runtime_path::RuntimeOrigin; const KIND: RuntimeKind = RuntimeKind::$kind; } diff --git a/runtime/integration-tests/src/generic/utils/genesis.rs b/runtime/integration-tests/src/generic/utils/genesis.rs index 8f46ab24a2..97fae41c1c 100644 --- a/runtime/integration-tests/src/generic/utils/genesis.rs +++ b/runtime/integration-tests/src/generic/utils/genesis.rs @@ -39,7 +39,7 @@ impl Genesis { } } -// Add GenesisBuild functions for initialize your pallets +// Add GenesisBuild functions for pallet initialization. pub fn balances(balance: Balance) -> impl GenesisBuild { pallet_balances::GenesisConfig:: {