From 8ca22181fdd6df45c536c65ca05ec3a43acf5145 Mon Sep 17 00:00:00 2001 From: Frederik Gartenmeister Date: Mon, 4 Sep 2023 15:59:02 +0200 Subject: [PATCH 1/2] fix: max orders per trading pair (#1524) --- runtime/development/src/lib.rs | 28 +--------- .../src/weights/pallet_order_book.rs | 54 +++++++++---------- 2 files changed, 28 insertions(+), 54 deletions(-) diff --git a/runtime/development/src/lib.rs b/runtime/development/src/lib.rs index 5f76d2728e..26a3860f96 100644 --- a/runtime/development/src/lib.rs +++ b/runtime/development/src/lib.rs @@ -1817,33 +1817,7 @@ impl pallet_transfer_allowlist::Config for Runtime { } parameter_types! { - pub const OrderPairVecSize: u32 = 1_000_000u32; -} - -// Minimum order amounts for orderbook orders v1 implementation. -// This will be replaced by runtime specifiable minimum, -// which will likely be set by governance. -const DEV_USDT_CURRENCY_ID: CurrencyId = CurrencyId::ForeignAsset(1); -const DEV_AUSD_CURRENCY_ID: CurrencyId = CurrencyId::ForeignAsset(2); -const DEV_USDT_DECIMALS: u128 = 1_000_000; -const DEV_AUSD_DECIMALS: u128 = 1_000_000_000_000; -const DEFAULT_DEV_MIN_ORDER: u128 = 5; -const MIN_DEV_USDT_ORDER: u128 = DEFAULT_DEV_MIN_ORDER * DEV_USDT_DECIMALS; -const MIN_DEV_AUSD_ORDER: u128 = DEFAULT_DEV_MIN_ORDER * DEV_AUSD_DECIMALS; -const MIN_DEV_NATIVE_ORDER: u128 = DEFAULT_DEV_MIN_ORDER * CFG; - -parameter_type_with_key! { - pub MinimumOrderAmount: |pair: (CurrencyId, CurrencyId)| -> Option { - match pair { - (CurrencyId::Native, DEV_AUSD_CURRENCY_ID) => Some(MIN_DEV_NATIVE_ORDER), - (DEV_AUSD_CURRENCY_ID, CurrencyId::Native) => Some(MIN_DEV_AUSD_ORDER), - (CurrencyId::Native, DEV_USDT_CURRENCY_ID) => Some(MIN_DEV_NATIVE_ORDER), - (DEV_USDT_CURRENCY_ID, CurrencyId::Native) => Some(MIN_DEV_USDT_ORDER), - (DEV_AUSD_CURRENCY_ID, DEV_USDT_CURRENCY_ID) => Some(MIN_DEV_AUSD_ORDER), - (DEV_USDT_CURRENCY_ID, DEV_AUSD_CURRENCY_ID) => Some(MIN_DEV_USDT_ORDER), - _ => None - } - }; + pub const OrderPairVecSize: u32 = 1_000u32; } impl pallet_order_book::Config for Runtime { diff --git a/runtime/development/src/weights/pallet_order_book.rs b/runtime/development/src/weights/pallet_order_book.rs index a4ef7881a2..16dedf49a9 100644 --- a/runtime/development/src/weights/pallet_order_book.rs +++ b/runtime/development/src/weights/pallet_order_book.rs @@ -2,9 +2,9 @@ //! Autogenerated weights for `pallet_order_book` //! //! THIS FILE WAS AUTO-GENERATED USING THE SUBSTRATE BENCHMARK CLI VERSION 4.0.0-dev -//! DATE: 2023-08-29, STEPS: `10`, REPEAT: `1`, LOW RANGE: `[]`, HIGH RANGE: `[]` +//! DATE: 2023-09-04, STEPS: `50`, REPEAT: `20`, LOW RANGE: `[]`, HIGH RANGE: `[]` //! WORST CASE MAP SIZE: `1000000` -//! HOSTNAME: `kf-FG`, CPU: `` +//! HOSTNAME: `kf-FG.local`, CPU: `` //! EXECUTION: Some(Wasm), WASM-EXECUTION: Compiled, CHAIN: Some("development-local"), DB CACHE: 1024 // Executed Command: @@ -12,14 +12,14 @@ // benchmark // pallet // --chain=development-local -// --steps=10 -// --repeat=1 +// --steps=50 +// --repeat=20 // --pallet=pallet-order-book // --extrinsic=* // --execution=wasm // --wasm-execution=compiled // --heap-pages=4096 -// --output=pallet-order-book.rs +// --output=pallet_order_book.rs #![cfg_attr(rustfmt, rustfmt_skip)] #![allow(unused_parens)] @@ -40,17 +40,17 @@ impl pallet_order_book::WeightInfo for WeightInfo { /// Storage: OrmlTokens Accounts (r:1 w:1) /// Proof: OrmlTokens Accounts (max_values: None, max_size: Some(129), added: 2604, mode: MaxEncodedLen) /// Storage: OrderBook AssetPairOrders (r:1 w:1) - /// Proof: OrderBook AssetPairOrders (max_values: None, max_size: Some(8000070), added: 8002545, mode: MaxEncodedLen) + /// Proof: OrderBook AssetPairOrders (max_values: None, max_size: Some(8068), added: 10543, mode: MaxEncodedLen) /// Storage: OrderBook Orders (r:0 w:1) /// Proof: OrderBook Orders (max_values: None, max_size: Some(186), added: 2661, mode: MaxEncodedLen) /// Storage: OrderBook UserOrders (r:0 w:1) /// Proof: OrderBook UserOrders (max_values: None, max_size: Some(226), added: 2701, mode: MaxEncodedLen) fn create_order() -> Weight { // Proof Size summary in bytes: - // Measured: `1217` - // Estimated: `8014376` - // Minimum execution time: 46_000 nanoseconds. - Weight::from_parts(46_000_000, 8014376) + // Measured: `1250` + // Estimated: `22407` + // Minimum execution time: 42_000 nanoseconds. + Weight::from_parts(42_000_000, 22407) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(5)) } @@ -66,10 +66,10 @@ impl pallet_order_book::WeightInfo for WeightInfo { /// Proof: OrderBook UserOrders (max_values: None, max_size: Some(226), added: 2701, mode: MaxEncodedLen) fn user_update_order() -> Weight { // Proof Size summary in bytes: - // Measured: `1722` - // Estimated: `17195` - // Minimum execution time: 40_000 nanoseconds. - Weight::from_parts(40_000_000, 17195) + // Measured: `1755` + // Estimated: `17228` + // Minimum execution time: 39_000 nanoseconds. + Weight::from_parts(39_000_000, 17228) .saturating_add(T::DbWeight::get().reads(6)) .saturating_add(T::DbWeight::get().writes(3)) } @@ -78,15 +78,15 @@ impl pallet_order_book::WeightInfo for WeightInfo { /// Storage: OrmlTokens Accounts (r:1 w:1) /// Proof: OrmlTokens Accounts (max_values: None, max_size: Some(129), added: 2604, mode: MaxEncodedLen) /// Storage: OrderBook AssetPairOrders (r:1 w:1) - /// Proof: OrderBook AssetPairOrders (max_values: None, max_size: Some(8000070), added: 8002545, mode: MaxEncodedLen) + /// Proof: OrderBook AssetPairOrders (max_values: None, max_size: Some(8068), added: 10543, mode: MaxEncodedLen) /// Storage: OrderBook UserOrders (r:0 w:1) /// Proof: OrderBook UserOrders (max_values: None, max_size: Some(226), added: 2701, mode: MaxEncodedLen) fn user_cancel_order() -> Weight { // Proof Size summary in bytes: // Measured: `1116` - // Estimated: `8007810` - // Minimum execution time: 32_000 nanoseconds. - Weight::from_parts(32_000_000, 8007810) + // Estimated: `15808` + // Minimum execution time: 30_000 nanoseconds. + Weight::from_parts(31_000_000, 15808) .saturating_add(T::DbWeight::get().reads(3)) .saturating_add(T::DbWeight::get().writes(4)) } @@ -97,15 +97,15 @@ impl pallet_order_book::WeightInfo for WeightInfo { /// Storage: System Account (r:2 w:0) /// Proof: System Account (max_values: None, max_size: Some(128), added: 2603, mode: MaxEncodedLen) /// Storage: OrderBook AssetPairOrders (r:1 w:1) - /// Proof: OrderBook AssetPairOrders (max_values: None, max_size: Some(8000070), added: 8002545, mode: MaxEncodedLen) + /// Proof: OrderBook AssetPairOrders (max_values: None, max_size: Some(8068), added: 10543, mode: MaxEncodedLen) /// Storage: OrderBook UserOrders (r:0 w:1) /// Proof: OrderBook UserOrders (max_values: None, max_size: Some(226), added: 2701, mode: MaxEncodedLen) fn fill_order_full() -> Weight { // Proof Size summary in bytes: // Measured: `1702` - // Estimated: `8020828` + // Estimated: `28826` // Minimum execution time: 64_000 nanoseconds. - Weight::from_parts(64_000_000, 8020828) + Weight::from_parts(65_000_000, 28826) .saturating_add(T::DbWeight::get().reads(8)) .saturating_add(T::DbWeight::get().writes(7)) } @@ -115,8 +115,8 @@ impl pallet_order_book::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_000 nanoseconds. - Weight::from_ref_time(9_000_000) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(8_000_000) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: OrderBook TradingPair (r:0 w:1) @@ -125,8 +125,8 @@ impl pallet_order_book::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `0` // Estimated: `0` - // Minimum execution time: 9_000 nanoseconds. - Weight::from_ref_time(9_000_000) + // Minimum execution time: 7_000 nanoseconds. + Weight::from_ref_time(8_000_000) .saturating_add(T::DbWeight::get().writes(1)) } /// Storage: OrderBook TradingPair (r:1 w:1) @@ -135,8 +135,8 @@ impl pallet_order_book::WeightInfo for WeightInfo { // Proof Size summary in bytes: // Measured: `209` // Estimated: `2557` - // Minimum execution time: 14_000 nanoseconds. - Weight::from_parts(14_000_000, 2557) + // Minimum execution time: 11_000 nanoseconds. + Weight::from_parts(12_000_000, 2557) .saturating_add(T::DbWeight::get().reads(1)) .saturating_add(T::DbWeight::get().writes(1)) } From 4af6cfb830059929c10e1118442044964c906da7 Mon Sep 17 00:00:00 2001 From: Nuno Alexandre Date: Tue, 5 Sep 2023 15:18:16 +0200 Subject: [PATCH 2/2] xcm: Align config across all runtimes (#1527) * e2e: test cfg xcm transfers * dev: Align xcm CurrencyIdConvert with Centrifuge * Improve un-anchor * cfg-primitives: Add HashedDescriptionDescribeFamilyAllTerminal * xcm: Use HashedDescriptionDescribeFamilyAllTerminal Instead of `Account32Hash` --- Cargo.lock | 3 + libs/primitives/Cargo.toml | 11 +- libs/primitives/src/lib.rs | 158 +++++++++++ runtime/altair/src/xcm.rs | 34 +-- runtime/centrifuge/src/xcm.rs | 31 +-- runtime/development/src/xcm.rs | 81 ++---- .../liquidity_pools/pallet/development/mod.rs | 1 + .../pallet/development/tests/mod.rs | 2 +- .../pallet/development/transfers.rs | 245 ++++++++++++++++++ 9 files changed, 459 insertions(+), 107 deletions(-) create mode 100644 runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs diff --git a/Cargo.lock b/Cargo.lock index 66f3079ce5..67518c206b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1300,8 +1300,11 @@ dependencies = [ "sp-arithmetic", "sp-consensus-aura", "sp-core", + "sp-io", "sp-runtime", "sp-std", + "xcm", + "xcm-executor", ] [[package]] diff --git a/libs/primitives/Cargo.toml b/libs/primitives/Cargo.toml index 813edac75f..a7b9805a11 100644 --- a/libs/primitives/Cargo.toml +++ b/libs/primitives/Cargo.toml @@ -19,6 +19,7 @@ serde = { version = "1.0.119" } sp-arithmetic = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } sp-consensus-aura = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } sp-core = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } +sp-io = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } sp-runtime = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } sp-std = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } @@ -27,9 +28,13 @@ frame-support = { git = "https://github.com/paritytech/substrate", default-featu frame-system = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } pallet-collective = { git = "https://github.com/paritytech/substrate", default-features = false, branch = "polkadot-v0.9.38" } -# cumuluse primitives dependencies +# cumulus primitives dependencies cumulus-primitives-core = { git = "https://github.com/paritytech/cumulus", default-features = false, branch = "polkadot-v0.9.38" } +# XCM primitives dependencies +xcm = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38" } +xcm-executor = { git = "https://github.com/paritytech/polkadot", default-features = false, branch = "release-v0.9.38" } + [features] default = ["std"] runtime-benchmarks = [ @@ -37,10 +42,12 @@ runtime-benchmarks = [ "frame-system/runtime-benchmarks", "pallet-collective/runtime-benchmarks", "sp-runtime/runtime-benchmarks", + "xcm-executor/runtime-benchmarks", ] std = [ "serde/std", "codec/std", + "sp-io/std", "sp-std/std", "scale-info/std", "sp-core/std", @@ -51,6 +58,8 @@ std = [ "pallet-collective/std", "sp-consensus-aura/std", "cumulus-primitives-core/std", + "xcm/std", + "xcm-executor/std", ] try-runtime = [ "frame-support/try-runtime", diff --git a/libs/primitives/src/lib.rs b/libs/primitives/src/lib.rs index b8c0d3f28e..8940cabbb4 100644 --- a/libs/primitives/src/lib.rs +++ b/libs/primitives/src/lib.rs @@ -336,3 +336,161 @@ pub mod liquidity_pools { } } } + +pub mod xcm { + use codec::{Compact, Encode}; + use sp_io::hashing::blake2_256; + use sp_std::{borrow::Borrow, marker::PhantomData, vec::Vec}; + use xcm::prelude::{ + AccountId32, AccountKey20, Here, MultiLocation, PalletInstance, Parachain, X1, + }; + use xcm_executor::traits::Convert; + + /// NOTE: Copied from + /// + /// temporary struct that mimics the behavior of the upstream type that we + /// will move to once we update this repository to Polkadot 0.9.43+. + pub struct HashedDescriptionDescribeFamilyAllTerminal(PhantomData); + impl + Clone> HashedDescriptionDescribeFamilyAllTerminal { + fn describe_location_suffix(l: &MultiLocation) -> Result, ()> { + match (l.parents, &l.interior) { + (0, Here) => Ok(Vec::new()), + (0, X1(PalletInstance(i))) => { + Ok((b"Pallet", Compact::::from(*i as u32)).encode()) + } + (0, X1(AccountId32 { id, .. })) => Ok((b"AccountId32", id).encode()), + (0, X1(AccountKey20 { key, .. })) => Ok((b"AccountKey20", key).encode()), + _ => Err(()), + } + } + } + + impl + Clone> Convert + for HashedDescriptionDescribeFamilyAllTerminal + { + fn convert_ref(location: impl Borrow) -> Result { + let l = location.borrow(); + let to_hash = match (l.parents, l.interior.first()) { + (0, Some(Parachain(index))) => { + let tail = l.interior.split_first().0; + let interior = Self::describe_location_suffix(&tail.into())?; + (b"ChildChain", Compact::::from(*index), interior).encode() + } + (1, Some(Parachain(index))) => { + let tail = l.interior.split_first().0; + let interior = Self::describe_location_suffix(&tail.into())?; + (b"SiblingChain", Compact::::from(*index), interior).encode() + } + (1, _) => { + let tail = l.interior.into(); + let interior = Self::describe_location_suffix(&tail)?; + (b"ParentChain", interior).encode() + } + _ => return Err(()), + }; + Ok(blake2_256(&to_hash).into()) + } + + fn reverse_ref(_: impl Borrow) -> Result { + Err(()) + } + } + + #[test] + fn test_hashed_family_all_terminal_converter() { + use xcm::prelude::X2; + + type Converter = HashedDescriptionDescribeFamilyAllTerminal; + + assert_eq!( + [ + 129, 211, 14, 6, 146, 54, 225, 200, 135, 103, 248, 244, 125, 112, 53, 133, 91, 42, + 215, 236, 154, 199, 191, 208, 110, 148, 223, 55, 92, 216, 250, 34 + ], + Converter::<[u8; 32]>::convert(MultiLocation { + parents: 0, + interior: X2( + Parachain(1), + AccountId32 { + network: None, + id: [0u8; 32] + } + ), + }) + .unwrap() + ); + assert_eq!( + [ + 17, 142, 105, 253, 199, 34, 43, 136, 155, 48, 12, 137, 155, 219, 155, 110, 93, 181, + 93, 252, 124, 60, 250, 195, 229, 86, 31, 220, 121, 111, 254, 252 + ], + Converter::<[u8; 32]>::convert(MultiLocation { + parents: 1, + interior: X2( + Parachain(1), + AccountId32 { + network: None, + id: [0u8; 32] + } + ), + }) + .unwrap() + ); + assert_eq!( + [ + 237, 65, 190, 49, 53, 182, 196, 183, 151, 24, 214, 23, 72, 244, 235, 87, 187, 67, + 52, 122, 195, 192, 10, 58, 253, 49, 0, 112, 175, 224, 125, 66 + ], + Converter::<[u8; 32]>::convert(MultiLocation { + parents: 0, + interior: X2( + Parachain(1), + AccountKey20 { + network: None, + key: [0u8; 20] + } + ), + }) + .unwrap() + ); + assert_eq!( + [ + 226, 225, 225, 162, 254, 156, 113, 95, 68, 155, 160, 118, 126, 18, 166, 132, 144, + 19, 8, 204, 228, 112, 164, 189, 179, 124, 249, 1, 168, 110, 151, 50 + ], + Converter::<[u8; 32]>::convert(MultiLocation { + parents: 1, + interior: X2( + Parachain(1), + AccountKey20 { + network: None, + key: [0u8; 20] + } + ), + }) + .unwrap() + ); + assert_eq!( + [ + 254, 186, 179, 229, 13, 24, 84, 36, 84, 35, 64, 95, 114, 136, 62, 69, 247, 74, 215, + 104, 121, 114, 53, 6, 124, 46, 42, 245, 121, 197, 12, 208 + ], + Converter::<[u8; 32]>::convert(MultiLocation { + parents: 1, + interior: X2(Parachain(2), PalletInstance(3)), + }) + .unwrap() + ); + assert_eq!( + [ + 217, 56, 0, 36, 228, 154, 250, 26, 200, 156, 1, 39, 254, 162, 16, 187, 107, 67, 27, + 16, 218, 254, 250, 184, 6, 27, 216, 138, 194, 93, 23, 165 + ], + Converter::<[u8; 32]>::convert(MultiLocation { + parents: 1, + interior: Here, + }) + .unwrap() + ); + } +} diff --git a/runtime/altair/src/xcm.rs b/runtime/altair/src/xcm.rs index 59ef1bddf7..abbbbf86f1 100644 --- a/runtime/altair/src/xcm.rs +++ b/runtime/altair/src/xcm.rs @@ -11,7 +11,6 @@ // GNU General Public License for more details. use cfg_primitives::{ - constants::currency_decimals, parachains, types::{EnsureRootOr, HalfOfCouncil}, }; @@ -33,7 +32,7 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use runtime_common::{ xcm::{general_key, AccountIdToMultiLocation, FixedConversionRateProvider}, - xcm_fees::{default_per_second, ksm_per_second, native_per_second}, + xcm_fees::native_per_second, }; use sp_core::ConstU32; use sp_runtime::traits::{Convert, Zero}; @@ -106,28 +105,6 @@ parameter_types! { native_per_second(), 0, ); - - pub AirPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::parachain_id().into()), general_key(parachains::kusama::altair::AIR_KEY)), - ).into(), - native_per_second(), - ); - - pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); - - pub AUSDPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::karura::ID), - general_key(parachains::kusama::karura::AUSD_KEY) - ) - ).into(), - default_per_second(currency_decimals::AUSD) - ); - } pub struct ToTreasury; @@ -223,10 +200,12 @@ impl xcm_executor::traits::Convert for CurrencyIdConv let unanchored_location = match location { MultiLocation { parents: 0, - interior: X1(x), + interior, } => MultiLocation { parents: 1, - interior: X2(Parachain(u32::from(ParachainInfo::get())), x), + interior: interior + .pushed_front_with(Parachain(u32::from(ParachainInfo::get()))) + .map_err(|_| location)?, }, x => x, }; @@ -283,7 +262,6 @@ impl pallet_xcm::Config for Runtime { } parameter_types! { - pub const KsmLocation: MultiLocation = MultiLocation::parent(); pub const RelayNetwork: NetworkId = NetworkId::Kusama; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); @@ -301,6 +279,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Generate remote accounts according to polkadot standards + cfg_primitives::xcm::HashedDescriptionDescribeFamilyAllTerminal, ); /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/runtime/centrifuge/src/xcm.rs b/runtime/centrifuge/src/xcm.rs index b69b2e88f8..0d241f26a3 100644 --- a/runtime/centrifuge/src/xcm.rs +++ b/runtime/centrifuge/src/xcm.rs @@ -11,7 +11,6 @@ // GNU General Public License for more details. use cfg_primitives::{ - constants::currency_decimals, parachains, types::{EnsureRootOr, HalfOfCouncil}, }; @@ -35,7 +34,7 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use runtime_common::{ xcm::{general_key, AccountIdToMultiLocation, FixedConversionRateProvider}, - xcm_fees::{default_per_second, native_per_second}, + xcm_fees::native_per_second, }; use sp_core::ConstU32; use sp_runtime::traits::{Convert, Zero}; @@ -108,26 +107,6 @@ parameter_types! { native_per_second(), 0, ); - - pub CfgPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::parachain_id().into()), general_key(parachains::polkadot::centrifuge::CFG_KEY)), - ).into(), - native_per_second(), - ); - - pub AUSDPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 1, - X2( - Parachain(parachains::polkadot::acala::ID), - general_key(parachains::polkadot::acala::AUSD_KEY) - ) - ).into(), - default_per_second(currency_decimals::AUSD) - ); - } pub struct ToTreasury; @@ -223,10 +202,12 @@ impl xcm_executor::traits::Convert for CurrencyIdConv let unanchored_location = match location { MultiLocation { parents: 0, - interior: X1(x), + interior, } => MultiLocation { parents: 1, - interior: X2(Parachain(u32::from(ParachainInfo::get())), x), + interior: interior + .pushed_front_with(Parachain(u32::from(ParachainInfo::get()))) + .map_err(|_| location)?, }, x => x, }; @@ -300,6 +281,8 @@ pub type LocationToAccountId = ( SiblingParachainConvertsVia, // Straight up local `AccountId32` origins just alias directly to `AccountId`. AccountId32Aliases, + // Generate remote accounts according to polkadot standards + cfg_primitives::xcm::HashedDescriptionDescribeFamilyAllTerminal, ); /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/runtime/development/src/xcm.rs b/runtime/development/src/xcm.rs index e894412001..d61bf3bac8 100644 --- a/runtime/development/src/xcm.rs +++ b/runtime/development/src/xcm.rs @@ -10,7 +10,6 @@ // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. use cfg_primitives::{ - constants::currency_decimals, parachains, types::{EnsureRootOr, HalfOfCouncil}, }; @@ -34,13 +33,13 @@ use pallet_xcm::XcmPassthrough; use polkadot_parachain::primitives::Sibling; use runtime_common::{ xcm::{general_key, AccountIdToMultiLocation, FixedConversionRateProvider, LpInstanceRelayer}, - xcm_fees::{default_per_second, ksm_per_second, native_per_second}, + xcm_fees::native_per_second, }; use sp_core::ConstU32; use sp_runtime::traits::{Convert, Zero}; use xcm::{latest::Weight as XcmWeight, prelude::*}; use xcm_builder::{ - Account32Hash, AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, ConvertedConcreteId, EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, FungiblesAdapter, NoChecking, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, @@ -108,27 +107,6 @@ parameter_types! { 0, ); - pub NativePerSecond: (AssetId, u128) = ( - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::parachain_id().into()), general_key(parachains::kusama::altair::AIR_KEY)), - ).into(), - native_per_second(), - ); - - pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); - - pub AUSDPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 1, - X2( - Parachain(parachains::kusama::karura::ID), - general_key(parachains::kusama::karura::AUSD_KEY) - ) - ).into(), - default_per_second(currency_decimals::AUSD) - ); - } pub struct ToTreasury; @@ -201,44 +179,41 @@ where /// This type implements conversions from our `CurrencyId` type into /// `MultiLocation` and vice-versa. A currency locally is identified with a /// `CurrencyId` variant but in the network it is identified in the form of a -/// `MultiLocation`, in this case a pair (Para-Id, Currency-Id). +/// `MultiLocation`. pub struct CurrencyIdConvert; /// Convert our `CurrencyId` type into its `MultiLocation` representation. -/// Other chains need to know how this conversion takes place in order to -/// handle it on their side. +/// We use the `OrmlAssetRegistry` to lookup the associated `MultiLocation` for +/// any given `CurrencyId`, while blocking tokens that are not Xcm-transferable. impl Convert> for CurrencyIdConvert { fn convert(id: CurrencyId) -> Option { - match id { - CurrencyId::Tranche(_, _) => None, - _ => OrmlAssetRegistry::multilocation(&id).ok()?, - } + OrmlAssetRegistry::metadata(id) + .filter(|m| m.additional.transferability.includes_xcm()) + .and_then(|m| m.location) + .and_then(|l| l.try_into().ok()) } } -/// Convert an incoming `MultiLocation` into a `CurrencyId` if possible. -/// Here we need to know the canonical representation of all the tokens we -/// handle in order to correctly convert their `MultiLocation` representation -/// into our internal `CurrencyId` type. +/// Convert an incoming `MultiLocation` into a `CurrencyId` through a +/// reverse-lookup using the OrmlAssetRegistry. In the registry, we register CFG +/// using its absolute, non-anchored MultliLocation so we need to unanchor the +/// input location for Centrifuge-native assets for that to work. impl xcm_executor::traits::Convert for CurrencyIdConvert { fn convert(location: MultiLocation) -> Result { - match location { + let unanchored_location = match location { MultiLocation { + parents: 0, + interior, + } => MultiLocation { parents: 1, - interior: X3(Parachain(para_id), PalletInstance(_), GeneralKey { .. }), - } => match para_id { - // Note: Until we have pools on Centrifuge, we don't know the pools pallet index - // and can't therefore match specifically on the Tranche tokens' multilocation; - // However, we can preemptively assume that any Centrifuge X3-based asset refers - // to a Tranche token and explicitly fail its conversion to avoid Tranche tokens - // from being transferred through XCM without permission checks. This is fine since - // we don't have any other native token represented as an X3 neither do we plan to. - id if id == u32::from(ParachainInfo::get()) => Err(location), - // Still support X3-based MultiLocations native to other chains - _ => OrmlAssetRegistry::location_to_asset_id(location).ok_or(location), + interior: interior + .pushed_front_with(Parachain(u32::from(ParachainInfo::get()))) + .map_err(|_| location)?, }, - _ => OrmlAssetRegistry::location_to_asset_id(location).ok_or(location), - } + x => x, + }; + + OrmlAssetRegistry::location_to_asset_id(unanchored_location).ok_or(location) } } @@ -290,8 +265,7 @@ impl pallet_xcm::Config for Runtime { } parameter_types! { - pub const KsmLocation: MultiLocation = MultiLocation::parent(); - pub const RelayNetwork: NetworkId = NetworkId::Kusama; + pub const RelayNetwork: NetworkId = NetworkId::Rococo; pub RelayChainOrigin: RuntimeOrigin = cumulus_pallet_xcm::Origin::Relay.into(); pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); pub CheckingAccount: AccountId = PolkadotXcm::check_account(); @@ -309,9 +283,8 @@ pub type LocationToAccountId = ( // If we receive a MultiLocation of type AccountId32 that is within Centrifuge, // just alias it to a local [AccountId]. AccountId32Aliases, - // A wildcard MultiLocation to AccountId conversion for all the other MultiLocations - // within the same Relay network. - Account32Hash, + // Generate remote accounts according to polkadot standards + cfg_primitives::xcm::HashedDescriptionDescribeFamilyAllTerminal, ); /// No local origins on this chain are allowed to dispatch XCM sends/executions. diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs index 34f13591a7..17909ac383 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/mod.rs @@ -1,3 +1,4 @@ mod setup; mod test_net; mod tests; +mod transfers; diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/mod.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/mod.rs index 7acc7e185a..3c69d462d8 100644 --- a/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/mod.rs +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/tests/mod.rs @@ -17,7 +17,7 @@ mod routers; /// Register AUSD in the asset registry. /// It should be executed within an externalities environment. -fn register_ausd() { +pub fn register_ausd() { let meta: AssetMetadata = AssetMetadata { decimals: 12, name: "Acala Dollar".into(), diff --git a/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs b/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs new file mode 100644 index 0000000000..58103dc838 --- /dev/null +++ b/runtime/integration-tests/src/liquidity_pools/pallet/development/transfers.rs @@ -0,0 +1,245 @@ +// Copyright 2021 Development GmbH (centrifuge.io). +// This file is part of Development chain project. +// +// Development 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). +// Development 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. + +// Copyright 2021 Development GmbH (centrifuge.io). +// This file is part of Development chain project. +// +// Development 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). +// Development 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::{constants::currency_decimals, parachains, Balance}; +use cfg_types::{ + tokens::{CrossChainTransferability, CurrencyId, CustomMetadata}, + xcm::XcmMetadata, +}; +use development_runtime::{Balances, OrmlAssetRegistry, OrmlTokens, RuntimeOrigin, XTokens}; +use frame_support::assert_ok; +use orml_traits::{asset_registry::AssetMetadata, FixedConversionRateProvider, MultiCurrency}; +use runtime_common::{ + xcm::general_key, + xcm_fees::{default_per_second, ksm_per_second}, +}; +use sp_runtime::traits::BadOrigin; +use xcm::{ + latest::{Junction, Junction::*, Junctions::*, MultiLocation, NetworkId, WeightLimit}, + VersionedMultiLocation, +}; +use xcm_emulator::TestExt; + +use crate::liquidity_pools::pallet::{ + development::{ + setup::{centrifuge_account, cfg, moonbeam_account, ALICE, BOB, CHARLIE, PARA_ID_MOONBEAM}, + test_net::{Development, Moonbeam, RelayChain, TestNet}, + tests::register_ausd, + }, + xcm_metadata, +}; + +/* + +NOTE: We hardcode the expected balances after an XCM operation given that the weights involved in +XCM execution often change slightly with each Polkadot update. We could simply test that the final +balance after some XCM operation is `initialBalance - amount - fee`, which would mean we would +never have to touch the tests again. However, by hard-coding these values we are forced to catch +an unexpectedly big change that would have a big impact on the weights and fees and thus balances, +which would go unnoticed and untreated otherwise. + + */ + +#[test] +fn transfer_cfg_to_sibling() { + TestNet::reset(); + + let alice_initial_balance = cfg(10_000); + let bob_initial_balance = cfg(10_000); + let transfer_amount = cfg(5); + let cfg_in_sibling = CurrencyId::ForeignAsset(12); + + // CFG Metadata + let meta: AssetMetadata = AssetMetadata { + decimals: 18, + name: "Development".into(), + symbol: "CFG".into(), + existential_deposit: 1_000_000_000_000, + location: Some(VersionedMultiLocation::V3(MultiLocation::new( + 1, + X2( + Parachain(parachains::polkadot::centrifuge::ID), + general_key(parachains::polkadot::centrifuge::CFG_KEY), + ), + ))), + additional: CustomMetadata { + transferability: CrossChainTransferability::Xcm(Default::default()), + ..CustomMetadata::default() + }, + }; + + Development::execute_with(|| { + assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); + assert_eq!(Balances::free_balance(&moonbeam_account()), 0); + + assert_ok!(OrmlAssetRegistry::register_asset( + RuntimeOrigin::root(), + meta.clone(), + Some(CurrencyId::Native), + )); + }); + + Moonbeam::execute_with(|| { + assert_eq!(OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), 0); + + assert_ok!(OrmlAssetRegistry::register_asset( + RuntimeOrigin::root(), + meta, + Some(cfg_in_sibling) + )); + }); + + Development::execute_with(|| { + assert_ok!(XTokens::transfer( + RuntimeOrigin::signed(ALICE.into()), + CurrencyId::Native, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(PARA_ID_MOONBEAM), + Junction::AccountId32 { + network: None, + id: BOB, + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Alice's balance is initial balance - amount transferred + assert_eq!( + Balances::free_balance(&ALICE.into()), + alice_initial_balance - transfer_amount + ); + + // Verify that the amount transferred is now part of the sibling account here + assert_eq!(Balances::free_balance(&moonbeam_account()), transfer_amount); + }); + + Moonbeam::execute_with(|| { + let current_balance = OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()); + + // Verify that BOB now has (amount transferred - fee) + assert_eq!(current_balance, transfer_amount - fee(18)); + + // Sanity check for the actual amount BOB ends up with + assert_eq!(current_balance, 4991987200000000000); + }); +} + +#[test] +fn transfer_cfg_sibling_to_centrifuge() { + TestNet::reset(); + + // In order to be able to transfer CFG from Moonbeam to Development, we need to + // first send CFG from Development to Moonbeam, or else it fails since it'd be + // like Moonbeam had minted CFG on their side. + transfer_cfg_to_sibling(); + + let alice_initial_balance = 9995000000000000000000; + let bob_initial_balance = cfg(5) - cfg_fee(); + let transfer_amount = cfg(4); + // Note: This asset was registered in `transfer_cfg_to_sibling` + let cfg_in_sibling = CurrencyId::ForeignAsset(12); + + Development::execute_with(|| { + assert_eq!(Balances::free_balance(&ALICE.into()), alice_initial_balance); + }); + + Moonbeam::execute_with(|| { + assert_eq!(Balances::free_balance(¢rifuge_account()), 0); + assert_eq!( + OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), + bob_initial_balance + ); + + assert_ok!(XTokens::transfer( + RuntimeOrigin::signed(BOB.into()), + cfg_in_sibling, + transfer_amount, + Box::new( + MultiLocation::new( + 1, + X2( + Parachain(parachains::polkadot::centrifuge::ID), + Junction::AccountId32 { + network: None, + id: CHARLIE, + } + ) + ) + .into() + ), + WeightLimit::Limited(8_000_000_000_000.into()), + )); + + // Confirm that Charlie's balance is initial balance - amount transferred + assert_eq!( + OrmlTokens::free_balance(cfg_in_sibling, &BOB.into()), + bob_initial_balance - transfer_amount + ); + }); + + Development::execute_with(|| { + // Verify that Charlie's balance equals the amount transferred - fee + assert_eq!( + Balances::free_balance(&CHARLIE.into()), + transfer_amount - cfg_fee(), + ); + }); +} + +#[test] +fn test_total_fee() { + assert_eq!(cfg_fee(), 8012800000000000); +} + +fn cfg_fee() -> Balance { + fee(currency_decimals::NATIVE) +} + +fn ausd_fee() -> Balance { + fee(currency_decimals::AUSD) +} + +fn fee(decimals: u32) -> Balance { + calc_fee(default_per_second(decimals)) +} + +// The fee associated with transferring DOT tokens +fn dot_fee() -> Balance { + fee(10) +} + +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. + // NOTE: it is possible that in different machines this value may differ. We + // shall see. + fee_per_second.div_euclid(10_000) * 8 +}