diff --git a/parachain/runtime/interlay/src/lib.rs b/parachain/runtime/interlay/src/lib.rs index 2056c1e3ec..3f0d9f6694 100644 --- a/parachain/runtime/interlay/src/lib.rs +++ b/parachain/runtime/interlay/src/lib.rs @@ -64,18 +64,11 @@ pub use primitives::{ }; // XCM imports -use cumulus_primitives_core::ParaId; -use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; -use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; -use polkadot_parachain::primitives::Sibling; -use xcm::latest::prelude::*; - -use xcm_builder::{ - AccountId32Aliases, AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, LocationInverter, - NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, - SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, -}; -use xcm_executor::{Config, XcmExecutor}; +use pallet_xcm::{EnsureXcm, IsMajorityOfBody}; +use xcm::opaque::latest::BodyId; +use xcm_config::ParentLocation; + +pub mod xcm_config; type VaultId = primitives::VaultId; @@ -633,257 +626,6 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} -parameter_types! { - pub const ParentLocation: MultiLocation = MultiLocation::parent(); - pub const ParentNetwork: NetworkId = NetworkId::Kusama; - pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -/// Means for transacting assets on this chain. -type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `Origin::Signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - XcmPassthrough, -); - -parameter_types! { - // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer. - pub UnitWeightCost: Weight = 200_000_000; -} - -pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom); - -parameter_types! { - pub const MaxInstructions: u32 = 100; -} - -pub struct XcmConfig; - -impl Config for XcmConfig { - type Call = Call; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = MultiNativeAsset; - type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation - type LocationInverter = LocationInverter; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = UsingComponents< - IdentityFee, - ParentLocation, - AccountId, - orml_tokens::CurrencyAdapter, - (), - >; - type ResponseHandler = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; -} - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = (SignedToAccountId32,); - -/// The means for routing XCM messages which are not for local execution into the right message -/// queues. -pub type XcmRouter = ( - // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, - // ..and XCMP to communicate with the sibling chains. - XcmpQueue, -); - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type Call = Call; - type Origin = Origin; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; - type ChannelInfo = ParachainSystem; - type VersionWrapper = PolkadotXcm; - type ExecuteOverweightOrigin = EnsureRoot; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = (); -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; -} - -pub type LocalAssetTransactor = MultiCurrencyAdapter< - Tokens, - UnknownTokens, - IsNativeConcrete, - AccountId, - LocationToAccountId, - CurrencyId, - CurrencyIdConvert, - DepositToAlternative, ->; - -pub use currency_id_convert::CurrencyIdConvert; - -mod currency_id_convert { - use super::*; - use codec::{Decode, Encode}; - - fn native_currency_location(id: CurrencyId) -> MultiLocation { - MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) - } - - pub struct CurrencyIdConvert; - - impl Convert> for CurrencyIdConvert { - fn convert(id: CurrencyId) -> Option { - match id { - PARENT_CURRENCY_ID => Some(MultiLocation::parent()), - WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), - NATIVE_CURRENCY_ID => Some(native_currency_location(id)), - _ => None, - } - } - } - - impl Convert> for CurrencyIdConvert { - fn convert(location: MultiLocation) -> Option { - fn decode_currency_id(key: Vec) -> Option { - // decode the general key - if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { - // check `currency_id` is cross-chain asset - match currency_id { - WRAPPED_CURRENCY_ID => Some(currency_id), - NATIVE_CURRENCY_ID => Some(currency_id), - _ => None, - } - } else { - None - } - } - - match location { - x if x == MultiLocation::parent() => Some(PARENT_CURRENCY_ID), - MultiLocation { - parents: 1, - interior: X2(Parachain(id), GeneralKey(key)), - } if ParaId::from(id) == ParachainInfo::get() => decode_currency_id(key), - MultiLocation { - // adapt for reanchor canonical location: https://github.com/paritytech/polkadot/pull/4470 - parents: 0, - interior: X1(GeneralKey(key)), - } => decode_currency_id(key), - _ => None, - } - } - } - - impl Convert> for CurrencyIdConvert { - fn convert(asset: MultiAsset) -> Option { - if let MultiAsset { - id: Concrete(location), .. - } = asset - { - Self::convert(location) - } else { - None - } - } - } -} - -parameter_types! { - pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); - pub const MaxAssetsForTransfer: usize = 2; -} - -parameter_type_with_key! { - // Only used for transferring parachain tokens to other parachains using DOT as fee currency. Currently we do not support this, hence return MAX. - // See: https://github.com/open-web3-stack/open-runtime-module-library/blob/cadcc9fb10b8212f92668138fc8f83dc0c53acf5/xtokens/README.md#transfer-multiple-currencies - pub ParachainMinFee: |location: MultiLocation| -> u128 { - #[allow(clippy::match_ref_pats)] // false positive - match (location.parents, location.first_interior()) { - _ => u128::MAX, - } - }; -} - -pub struct AccountIdToMultiLocation; - -impl Convert for AccountIdToMultiLocation { - fn convert(account: AccountId) -> MultiLocation { - X1(AccountId32 { - network: NetworkId::Any, - id: account.into(), - }) - .into() - } -} - -impl orml_xtokens::Config for Runtime { - type Event = Event; - type Balance = Balance; - type CurrencyId = CurrencyId; - type CurrencyIdConvert = CurrencyIdConvert; - type AccountIdToMultiLocation = AccountIdToMultiLocation; - type SelfLocation = SelfLocation; - type XcmExecutor = XcmExecutor; - type Weigher = FixedWeightBounds; - type BaseXcmWeight = UnitWeightCost; - type LocationInverter = ::LocationInverter; - type MaxAssetsForTransfer = MaxAssetsForTransfer; - type MinXcmFee = ParachainMinFee; - type MultiLocationsFilter = Everything; - type ReserveProvider = AbsoluteReserveProvider; -} - impl orml_unknown_tokens::Config for Runtime { type Event = Event; } diff --git a/parachain/runtime/interlay/src/xcm_config.rs b/parachain/runtime/interlay/src/xcm_config.rs new file mode 100644 index 0000000000..d27c0b3489 --- /dev/null +++ b/parachain/runtime/interlay/src/xcm_config.rs @@ -0,0 +1,263 @@ +use super::*; +use cumulus_primitives_core::ParaId; +use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowTopLevelPaidExecutionFrom, EnsureXcmOrigin, FixedWeightBounds, LocationInverter, + NativeAsset, ParentIsPreset, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, + SignedAccountId32AsNative, SignedToAccountId32, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, +}; +use xcm_executor::{Config, XcmExecutor}; + +parameter_types! { + pub const ParentLocation: MultiLocation = MultiLocation::parent(); + pub const ParentNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +/// Means for transacting assets on this chain. +type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer. + pub UnitWeightCost: Weight = 200_000_000; +} + +pub type Barrier = (TakeWeightCredit, AllowTopLevelPaidExecutionFrom); + +parameter_types! { + pub const MaxInstructions: u32 = 100; +} + +pub struct XcmConfig; + +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = MultiNativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = UsingComponents< + IdentityFee, + ParentLocation, + AccountId, + orml_tokens::CurrencyAdapter, + (), + >; + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = (SignedToAccountId32,); + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type Call = Call; + type Origin = Origin; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + UnknownTokens, + IsNativeConcrete, + AccountId, + LocationToAccountId, + CurrencyId, + CurrencyIdConvert, + DepositToAlternative, +>; + +pub use currency_id_convert::CurrencyIdConvert; + +mod currency_id_convert { + use super::*; + use codec::{Decode, Encode}; + + fn native_currency_location(id: CurrencyId) -> MultiLocation { + MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) + } + + pub struct CurrencyIdConvert; + + impl Convert> for CurrencyIdConvert { + fn convert(id: CurrencyId) -> Option { + match id { + PARENT_CURRENCY_ID => Some(MultiLocation::parent()), + WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), + NATIVE_CURRENCY_ID => Some(native_currency_location(id)), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + fn decode_currency_id(key: Vec) -> Option { + // decode the general key + if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { + // check `currency_id` is cross-chain asset + match currency_id { + WRAPPED_CURRENCY_ID => Some(currency_id), + NATIVE_CURRENCY_ID => Some(currency_id), + _ => None, + } + } else { + None + } + } + + match location { + x if x == MultiLocation::parent() => Some(PARENT_CURRENCY_ID), + MultiLocation { + parents: 1, + interior: X2(Parachain(id), GeneralKey(key)), + } if ParaId::from(id) == ParachainInfo::get() => decode_currency_id(key), + MultiLocation { + // adapt for reanchor canonical location: https://github.com/paritytech/polkadot/pull/4470 + parents: 0, + interior: X1(GeneralKey(key)), + } => decode_currency_id(key), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(asset: MultiAsset) -> Option { + if let MultiAsset { + id: Concrete(location), .. + } = asset + { + Self::convert(location) + } else { + None + } + } + } +} + +parameter_types! { + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); + pub const MaxAssetsForTransfer: usize = 2; +} + +parameter_type_with_key! { + // Only used for transferring parachain tokens to other parachains using DOT as fee currency. Currently we do not support this, hence return MAX. + // See: https://github.com/open-web3-stack/open-runtime-module-library/blob/cadcc9fb10b8212f92668138fc8f83dc0c53acf5/xtokens/README.md#transfer-multiple-currencies + pub ParachainMinFee: |location: MultiLocation| -> u128 { + #[allow(clippy::match_ref_pats)] // false positive + match (location.parents, location.first_interior()) { + _ => u128::MAX, + } + }; +} + +pub struct AccountIdToMultiLocation; + +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} + +impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = UnitWeightCost; + type LocationInverter = ::LocationInverter; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = AbsoluteReserveProvider; +} diff --git a/parachain/runtime/kintsugi/src/lib.rs b/parachain/runtime/kintsugi/src/lib.rs index 6fe581838c..a9014a6cc2 100644 --- a/parachain/runtime/kintsugi/src/lib.rs +++ b/parachain/runtime/kintsugi/src/lib.rs @@ -9,7 +9,6 @@ include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs")); use bitcoin::types::H256Le; -use codec::Encode; use currency::Amount; use frame_support::{ dispatch::{DispatchError, DispatchResult}, @@ -23,7 +22,7 @@ use frame_system::{ limits::{BlockLength, BlockWeights}, EnsureRoot, RawOrigin, }; -use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key, MultiCurrency}; +use orml_traits::parameter_type_with_key; use pallet_transaction_payment::{Multiplier, TargetedFeeAdjustment}; use sp_api::impl_runtime_apis; use sp_core::{OpaqueMetadata, H256}; @@ -64,20 +63,7 @@ pub use primitives::{ SignedFixedPoint, SignedInner, UnsignedFixedPoint, UnsignedInner, KBTC, KINT, KSM, }; -// XCM imports -use cumulus_primitives_core::ParaId; -use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; -use pallet_xcm::XcmPassthrough; -use polkadot_parachain::primitives::Sibling; -use xcm::latest::prelude::*; - -use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, LocationInverter, NativeAsset, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, -}; -use xcm_executor::{Config, XcmExecutor}; +pub mod xcm_config; type VaultId = primitives::VaultId; @@ -600,327 +586,6 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} -parameter_types! { - pub const ParentLocation: MultiLocation = MultiLocation::parent(); - pub const ParentNetwork: NetworkId = NetworkId::Kusama; - pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -/// Means for transacting assets on this chain. -type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `Origin::Signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - XcmPassthrough, -); - -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - AllowKnownQueryResponses, - AllowSubscriptionsFrom, -); // required for others to keep track of our xcm version - -parameter_types! { - // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer. - pub UnitWeightCost: Weight = 200_000_000; - pub const MaxInstructions: u32 = 100; -} -pub struct XcmConfig; - -// the ksm cost to to execute a no-op extrinsic -fn base_tx_in_ksm() -> Balance { - KSM.one() / 50_000 -} -pub fn ksm_per_second() -> u128 { - let base_weight = Balance::from(ExtrinsicBaseWeight::get()); - let base_tx_per_second = (WEIGHT_PER_SECOND as u128) / base_weight; - base_tx_per_second * base_tx_in_ksm() -} - -parameter_types! { - pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); - pub KintPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KINT).encode())), - ).into(), - // KINT:KSM = 4:3 - (ksm_per_second() * 4) / 3 - ); - pub KbtcPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KBTC).encode())), - ).into(), - // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 - ksm_per_second() / 1_500_000 - ); - pub CanonicalizedKintPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 0, - X1(GeneralKey(Token(KINT).encode())), - ).into(), - // KINT:KSM = 4:3 - (ksm_per_second() * 4) / 3 - ); - pub CanonicalizedKbtcPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 0, - X1(GeneralKey(Token(KBTC).encode())), - ).into(), - // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 - ksm_per_second() / 1_500_000 - ); -} - -parameter_types! { - pub KintsugiTreasuryAccount: AccountId = TreasuryPalletId::get().into_account(); -} - -pub struct ToTreasury; -impl TakeRevenue for ToTreasury { - fn take_revenue(revenue: MultiAsset) { - if let MultiAsset { - id: Concrete(location), - fun: Fungible(amount), - } = revenue - { - if let Some(currency_id) = CurrencyIdConvert::convert(location) { - // Note: we should ensure that treasury account has existenial deposit for all of the cross-chain asset. - // Ignore the result. - let _ = Tokens::deposit(currency_id, &KintsugiTreasuryAccount::get(), amount); - } - } - } -} - -pub type Trader = ( - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, -); - -impl Config for XcmConfig { - type Call = Call; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = MultiNativeAsset; - type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation - type LocationInverter = LocationInverter; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = Trader; - type ResponseHandler = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; -} - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = (SignedToAccountId32,); - -/// The means for routing XCM messages which are not for local execution into the right message -/// queues. -pub type XcmRouter = ( - // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, - // ..and XCMP to communicate with the sibling chains. - XcmpQueue, -); - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type Call = Call; - type Origin = Origin; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; - type ChannelInfo = ParachainSystem; - type VersionWrapper = PolkadotXcm; - type ExecuteOverweightOrigin = EnsureRoot; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = (); -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; -} - -pub type LocalAssetTransactor = MultiCurrencyAdapter< - Tokens, - UnknownTokens, - IsNativeConcrete, - AccountId, - LocationToAccountId, - CurrencyId, - CurrencyIdConvert, - DepositToAlternative, ->; - -pub use currency_id_convert::CurrencyIdConvert; - -mod currency_id_convert { - use super::*; - use codec::{Decode, Encode}; - - fn native_currency_location(id: CurrencyId) -> MultiLocation { - MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) - } - - pub struct CurrencyIdConvert; - - impl Convert> for CurrencyIdConvert { - fn convert(id: CurrencyId) -> Option { - match id { - PARENT_CURRENCY_ID => Some(MultiLocation::parent()), - WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), - NATIVE_CURRENCY_ID => Some(native_currency_location(id)), - _ => None, - } - } - } - - impl Convert> for CurrencyIdConvert { - fn convert(location: MultiLocation) -> Option { - fn decode_currency_id(key: Vec) -> Option { - // decode the general key - if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { - // check `currency_id` is cross-chain asset - match currency_id { - WRAPPED_CURRENCY_ID => Some(currency_id), - NATIVE_CURRENCY_ID => Some(currency_id), - _ => None, - } - } else { - None - } - } - - match location { - x if x == MultiLocation::parent() => Some(PARENT_CURRENCY_ID), - MultiLocation { - parents: 1, - interior: X2(Parachain(id), GeneralKey(key)), - } if ParaId::from(id) == ParachainInfo::get() => decode_currency_id(key), - MultiLocation { - // adapt for reanchor canonical location: https://github.com/paritytech/polkadot/pull/4470 - parents: 0, - interior: X1(GeneralKey(key)), - } => decode_currency_id(key), - _ => None, - } - } - } - - impl Convert> for CurrencyIdConvert { - fn convert(asset: MultiAsset) -> Option { - if let MultiAsset { - id: Concrete(location), .. - } = asset - { - Self::convert(location) - } else { - None - } - } - } -} - -parameter_types! { - pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); - pub const MaxAssetsForTransfer: usize = 2; // potentially useful to send both kint and kbtc at once -} - -parameter_type_with_key! { - // Only used for transferring parachain tokens to other parachains using KSM as fee currency. Currently we do not support this, hence return MAX. - // See: https://github.com/open-web3-stack/open-runtime-module-library/blob/cadcc9fb10b8212f92668138fc8f83dc0c53acf5/xtokens/README.md#transfer-multiple-currencies - pub ParachainMinFee: |location: MultiLocation| -> u128 { - #[allow(clippy::match_ref_pats)] // false positive - match (location.parents, location.first_interior()) { - _ => u128::MAX, - } - }; -} - -pub struct AccountIdToMultiLocation; - -impl Convert for AccountIdToMultiLocation { - fn convert(account: AccountId) -> MultiLocation { - X1(AccountId32 { - network: NetworkId::Any, - id: account.into(), - }) - .into() - } -} - -impl orml_xtokens::Config for Runtime { - type Event = Event; - type Balance = Balance; - type CurrencyId = CurrencyId; - type CurrencyIdConvert = CurrencyIdConvert; - type AccountIdToMultiLocation = AccountIdToMultiLocation; - type SelfLocation = SelfLocation; - type XcmExecutor = XcmExecutor; - type Weigher = FixedWeightBounds; - type BaseXcmWeight = UnitWeightCost; - type LocationInverter = ::LocationInverter; - type MaxAssetsForTransfer = MaxAssetsForTransfer; - type MinXcmFee = ParachainMinFee; - type MultiLocationsFilter = Everything; - type ReserveProvider = AbsoluteReserveProvider; -} - impl orml_unknown_tokens::Config for Runtime { type Event = Event; } diff --git a/parachain/runtime/kintsugi/src/xcm_config.rs b/parachain/runtime/kintsugi/src/xcm_config.rs new file mode 100644 index 0000000000..bf6fb0432f --- /dev/null +++ b/parachain/runtime/kintsugi/src/xcm_config.rs @@ -0,0 +1,339 @@ +use super::*; +use codec::Encode; +use cumulus_primitives_core::ParaId; +use frame_support::{ + parameter_types, + traits::{Everything, Get, Nothing}, + weights::Weight, +}; +use orml_traits::{location::AbsoluteReserveProvider, parameter_type_with_key, MultiCurrency}; +use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, LocationInverter, NativeAsset, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, +}; +use xcm_executor::{Config, XcmExecutor}; + +parameter_types! { + pub const ParentLocation: MultiLocation = MultiLocation::parent(); + pub const ParentNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +/// Means for transacting assets on this chain. +type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowKnownQueryResponses, + AllowSubscriptionsFrom, +); // required for others to keep track of our xcm version + +parameter_types! { + // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer. + pub UnitWeightCost: Weight = 200_000_000; + pub const MaxInstructions: u32 = 100; +} + +pub struct XcmConfig; + +// the ksm cost to to execute a no-op extrinsic +fn base_tx_in_ksm() -> Balance { + KSM.one() / 50_000 +} + +pub fn ksm_per_second() -> u128 { + let base_weight = Balance::from(ExtrinsicBaseWeight::get()); + let base_tx_per_second = (WEIGHT_PER_SECOND as u128) / base_weight; + base_tx_per_second * base_tx_in_ksm() +} + +parameter_types! { + pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); + pub KintPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 + MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KINT).encode())), + ).into(), + // KINT:KSM = 4:3 + (ksm_per_second() * 4) / 3 + ); + pub KbtcPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 + MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KBTC).encode())), + ).into(), + // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 + ksm_per_second() / 1_500_000 + ); + pub CanonicalizedKintPerSecond: (AssetId, u128) = ( + MultiLocation::new( + 0, + X1(GeneralKey(Token(KINT).encode())), + ).into(), + // KINT:KSM = 4:3 + (ksm_per_second() * 4) / 3 + ); + pub CanonicalizedKbtcPerSecond: (AssetId, u128) = ( + MultiLocation::new( + 0, + X1(GeneralKey(Token(KBTC).encode())), + ).into(), + // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 + ksm_per_second() / 1_500_000 + ); +} + +pub struct ToTreasury; +impl TakeRevenue for ToTreasury { + fn take_revenue(revenue: MultiAsset) { + if let MultiAsset { + id: Concrete(location), + fun: Fungible(amount), + } = revenue + { + if let Some(currency_id) = CurrencyIdConvert::convert(location) { + // Note: we should ensure that treasury account has existential deposit for all of the cross-chain + // asset. Ignore the result. + let _ = Tokens::deposit(currency_id, &TreasuryAccount::get(), amount); + } + } + } +} + +pub type Trader = ( + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, +); + +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = MultiNativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = Trader; + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = (SignedToAccountId32,); + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type Call = Call; + type Origin = Origin; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + UnknownTokens, + IsNativeConcrete, + AccountId, + LocationToAccountId, + CurrencyId, + CurrencyIdConvert, + DepositToAlternative, +>; + +pub use currency_id_convert::CurrencyIdConvert; + +mod currency_id_convert { + use super::*; + use codec::{Decode, Encode}; + + fn native_currency_location(id: CurrencyId) -> MultiLocation { + MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) + } + + pub struct CurrencyIdConvert; + + impl Convert> for CurrencyIdConvert { + fn convert(id: CurrencyId) -> Option { + match id { + PARENT_CURRENCY_ID => Some(MultiLocation::parent()), + WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), + NATIVE_CURRENCY_ID => Some(native_currency_location(id)), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + fn decode_currency_id(key: Vec) -> Option { + // decode the general key + if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { + // check `currency_id` is cross-chain asset + match currency_id { + WRAPPED_CURRENCY_ID => Some(currency_id), + NATIVE_CURRENCY_ID => Some(currency_id), + _ => None, + } + } else { + None + } + } + + match location { + x if x == MultiLocation::parent() => Some(PARENT_CURRENCY_ID), + MultiLocation { + parents: 1, + interior: X2(Parachain(id), GeneralKey(key)), + } if ParaId::from(id) == ParachainInfo::get() => decode_currency_id(key), + MultiLocation { + // adapt for reanchor canonical location: https://github.com/paritytech/polkadot/pull/4470 + parents: 0, + interior: X1(GeneralKey(key)), + } => decode_currency_id(key), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(asset: MultiAsset) -> Option { + if let MultiAsset { + id: Concrete(location), .. + } = asset + { + Self::convert(location) + } else { + None + } + } + } +} + +parameter_types! { + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); + pub const MaxAssetsForTransfer: usize = 2; // potentially useful to send both kint and kbtc at once +} + +parameter_type_with_key! { + // Only used for transferring parachain tokens to other parachains using KSM as fee currency. Currently we do not support this, hence return MAX. + // See: https://github.com/open-web3-stack/open-runtime-module-library/blob/cadcc9fb10b8212f92668138fc8f83dc0c53acf5/xtokens/README.md#transfer-multiple-currencies + pub ParachainMinFee: |location: MultiLocation| -> u128 { + #[allow(clippy::match_ref_pats)] // false positive + match (location.parents, location.first_interior()) { + _ => u128::MAX, + } + }; +} + +pub struct AccountIdToMultiLocation; + +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} + +impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = UnitWeightCost; + type LocationInverter = ::LocationInverter; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = AbsoluteReserveProvider; +} diff --git a/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs b/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs index 2b650d513c..efbdbfb99c 100644 --- a/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs +++ b/parachain/runtime/runtime-tests/src/relaychain/kusama_cross_chain_transfer.rs @@ -31,10 +31,7 @@ fn transfer_from_relay_chain() { Tokens::free_balance(Token(KSM), &AccountId::from(BOB)), KSM.one() - xcm_fee ); - assert_eq!( - Tokens::free_balance(Token(KSM), &KintsugiTreasuryAccount::get()), - xcm_fee - ); + assert_eq!(Tokens::free_balance(Token(KSM), &TreasuryAccount::get()), xcm_fee); }); } @@ -151,10 +148,7 @@ fn transfer_to_sibling() { 95_000_000_000_000 - xcm_fee ); - assert_eq!( - Tokens::free_balance(Token(KINT), &KintsugiTreasuryAccount::get()), - xcm_fee - ); + assert_eq!(Tokens::free_balance(Token(KINT), &TreasuryAccount::get()), xcm_fee); assert_eq!( Tokens::free_balance(Token(KINT), &sibling_sovereign_account()), @@ -339,7 +333,7 @@ fn trap_assets_works() { assert_ok!(Tokens::deposit(Token(KSM), &parent_account, 100 * KSM.one())); assert_ok!(Tokens::deposit(Token(KINT), &parent_account, 100 * KINT.one())); - kint_treasury_amount = Tokens::free_balance(Token(KINT), &KintsugiTreasuryAccount::get()); + kint_treasury_amount = Tokens::free_balance(Token(KINT), &TreasuryAccount::get()); }); let assets: MultiAsset = (Parent, ksm_asset_amount).into(); @@ -394,11 +388,11 @@ fn trap_assets_works() { // unchanged treasury amounts assert_eq!( trader_weight_to_treasury, - Tokens::free_balance(Token(KSM), &KintsugiTreasuryAccount::get()) + Tokens::free_balance(Token(KSM), &TreasuryAccount::get()) ); assert_eq!( kint_treasury_amount, - Tokens::free_balance(Token(KINT), &KintsugiTreasuryAccount::get()) + Tokens::free_balance(Token(KINT), &TreasuryAccount::get()) ); }); diff --git a/parachain/runtime/runtime-tests/src/relaychain/kusama_test_net.rs b/parachain/runtime/runtime-tests/src/relaychain/kusama_test_net.rs index 10b6fe0d3b..54d649233d 100644 --- a/parachain/runtime/runtime-tests/src/relaychain/kusama_test_net.rs +++ b/parachain/runtime/runtime-tests/src/relaychain/kusama_test_net.rs @@ -123,7 +123,7 @@ pub fn para_ext(parachain_id: u32) -> sp_io::TestExternalities { ExtBuilder::default() .balances(vec![ (AccountId::from(ALICE), Token(KSM), 10 * KSM.one()), - // (kintsugi_runtime_parachain::KintsugiTreasuryAccount::get(), Token(KSM), KSM.one()), + // (kintsugi_runtime_parachain::TreasuryAccount::get(), Token(KSM), KSM.one()), ]) .parachain_id(parachain_id) .build() diff --git a/parachain/runtime/runtime-tests/src/setup.rs b/parachain/runtime/runtime-tests/src/setup.rs index e97df4db0c..d591f82d72 100644 --- a/parachain/runtime/runtime-tests/src/setup.rs +++ b/parachain/runtime/runtime-tests/src/setup.rs @@ -14,14 +14,9 @@ pub use xcm_emulator::XcmExecutor; pub use kintsugi_imports::*; mod kintsugi_imports { pub use frame_support::{parameter_types, weights::Weight}; - pub use kintsugi_runtime_parachain::*; - pub use sp_runtime::{traits::AccountIdConversion, FixedPointNumber}; - - parameter_types! { - pub TreasuryAccount: AccountId = TreasuryPalletId::get().into_account(); - } - + pub use kintsugi_runtime_parachain::{xcm_config::*, *}; pub use primitives::{CurrencyId::Token, KINT, KSM}; + pub use sp_runtime::{traits::AccountIdConversion, FixedPointNumber}; } pub const KINTSUGI_PARA_ID: u32 = 2092; diff --git a/parachain/runtime/testnet/src/lib.rs b/parachain/runtime/testnet/src/lib.rs index 3ef1d4b63d..a94d134839 100644 --- a/parachain/runtime/testnet/src/lib.rs +++ b/parachain/runtime/testnet/src/lib.rs @@ -66,19 +66,11 @@ pub use primitives::{ }; // XCM imports -use cumulus_primitives_core::ParaId; -use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; -use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; -use polkadot_parachain::primitives::Sibling; -use xcm::latest::prelude::*; - -use xcm_builder::{ - AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, - EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, LocationInverter, NativeAsset, ParentIsPreset, - RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, - SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, -}; -use xcm_executor::{Config, XcmExecutor}; +use pallet_xcm::{EnsureXcm, IsMajorityOfBody}; +use xcm::opaque::latest::BodyId; +use xcm_config::ParentLocation; + +pub mod xcm_config; type VaultId = primitives::VaultId; @@ -605,351 +597,6 @@ impl parachain_info::Config for Runtime {} impl cumulus_pallet_aura_ext::Config for Runtime {} -parameter_types! { - pub const ParentLocation: MultiLocation = MultiLocation::parent(); - pub const ParentNetwork: NetworkId = NetworkId::Kusama; - pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); - pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); -} - -/// Means for transacting assets on this chain. -type LocationToAccountId = ( - // The parent (Relay-chain) origin converts to the default `AccountId`. - ParentIsPreset, - // Sibling parachain origins convert to AccountId via the `ParaId::into`. - SiblingParachainConvertsVia, - // Straight up local `AccountId32` origins just alias directly to `AccountId`. - AccountId32Aliases, -); - -/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, -/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can -/// biases the kind of local `Origin` it will become. -pub type XcmOriginToTransactDispatchOrigin = ( - // Sovereign account converter; this attempts to derive an `AccountId` from the origin location - // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for - // foreign chains who want to have a local sovereign account on this chain which they control. - SovereignSignedViaLocation, - // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when - // recognised. - RelayChainAsNative, - // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when - // recognised. - SiblingParachainAsNative, - // Native signed account converter; this just converts an `AccountId32` origin into a normal - // `Origin::Signed` origin of the same 32-byte value. - SignedAccountId32AsNative, - // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. - XcmPassthrough, -); - -parameter_types! { - // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer. - pub UnitWeightCost: Weight = 200_000_000; -} - -pub type Barrier = ( - TakeWeightCredit, - AllowTopLevelPaidExecutionFrom, - AllowKnownQueryResponses, - AllowSubscriptionsFrom, -); // required for others to keep track of our xcm version - -parameter_types! { - pub const MaxInstructions: u32 = 100; -} -pub struct XcmConfig; - -// the ksm cost to to execute a no-op extrinsic -fn base_tx_in_ksm() -> Balance { - KSM.one() / 50_000 -} -pub fn ksm_per_second() -> u128 { - let base_weight = Balance::from(ExtrinsicBaseWeight::get()); - let base_tx_per_second = (WEIGHT_PER_SECOND as u128) / base_weight; - base_tx_per_second * base_tx_in_ksm() -} - -parameter_types! { - pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); - pub KintPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KINT).encode())), - ).into(), - // KINT:KSM = 4:3 - (ksm_per_second() * 4) / 3 - ); - pub KbtcPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 - MultiLocation::new( - 1, - X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KBTC).encode())), - ).into(), - // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 - ksm_per_second() / 1_500_000 - ); - pub CanonicalizedKintPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 0, - X1(GeneralKey(Token(KINT).encode())), - ).into(), - // KINT:KSM = 4:3 - (ksm_per_second() * 4) / 3 - ); - pub CanonicalizedKbtcPerSecond: (AssetId, u128) = ( - MultiLocation::new( - 0, - X1(GeneralKey(Token(KBTC).encode())), - ).into(), - // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 - ksm_per_second() / 1_500_000 - ); -} - -parameter_types! { - pub KintsugiTreasuryAccount: AccountId = TreasuryPalletId::get().into_account(); -} - -pub struct ToTreasury; -impl TakeRevenue for ToTreasury { - fn take_revenue(revenue: MultiAsset) { - if let MultiAsset { - id: Concrete(location), - fun: Fungible(amount), - } = revenue - { - if let Some(currency_id) = CurrencyIdConvert::convert(location) { - // Note: we should ensure that treasury account has existenial deposit for all of the cross-chain asset. - // Ignore the result. - let _ = Tokens::deposit(currency_id, &KintsugiTreasuryAccount::get(), amount); - } - } - } -} - -pub type Trader = ( - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FixedRateOfFungible, - FreeTestExection, -); - -// If all other trader items fail to apply, then execute for free. This is useful in in xcm -// testing: it allows the sibling to place the assets in UnknownTokens so that it can send -// them back. SHOULD NOT BE INCLUDED IN MAINNET -pub struct FreeTestExection; -impl WeightTrader for FreeTestExection { - fn new() -> Self { - Self - } - - fn buy_weight(&mut self, _weight: Weight, payment: Assets) -> Result { - log::warn!("xcm trader fallthrough: allowing free execution (TESTING ONLY)"); - Ok(payment) - } - - fn refund_weight(&mut self, _weight: Weight) -> Option { - log::warn!("xcm trader refund fallthrough: giving no refund (TESTING ONLY)"); - None - } -} - -impl Config for XcmConfig { - type Call = Call; - type XcmSender = XcmRouter; - // How to withdraw and deposit an asset. - type AssetTransactor = LocalAssetTransactor; - type OriginConverter = XcmOriginToTransactDispatchOrigin; - type IsReserve = MultiNativeAsset; - type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation - type LocationInverter = LocationInverter; - type Barrier = Barrier; - type Weigher = FixedWeightBounds; - type Trader = Trader; - type ResponseHandler = PolkadotXcm; - type SubscriptionService = PolkadotXcm; - type AssetTrap = PolkadotXcm; - type AssetClaims = PolkadotXcm; -} - -/// No local origins on this chain are allowed to dispatch XCM sends/executions. -pub type LocalOriginToLocation = (SignedToAccountId32,); - -/// The means for routing XCM messages which are not for local execution into the right message -/// queues. -pub type XcmRouter = ( - // Two routers - use UMP to communicate with the relay chain: - cumulus_primitives_utility::ParentAsUmp, - // ..and XCMP to communicate with the sibling chains. - XcmpQueue, -); - -impl pallet_xcm::Config for Runtime { - type Event = Event; - type Call = Call; - type Origin = Origin; - type SendXcmOrigin = EnsureXcmOrigin; - type XcmRouter = XcmRouter; - type ExecuteXcmOrigin = EnsureXcmOrigin; - type XcmExecuteFilter = Nothing; - type XcmExecutor = XcmExecutor; - type XcmTeleportFilter = Everything; - type XcmReserveTransferFilter = Everything; - type Weigher = FixedWeightBounds; - type LocationInverter = LocationInverter; - type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; - const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; -} - -impl cumulus_pallet_xcm::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; -} - -impl cumulus_pallet_xcmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; - type ChannelInfo = ParachainSystem; - type VersionWrapper = PolkadotXcm; - type ExecuteOverweightOrigin = EnsureRoot; - type ControllerOrigin = EnsureRoot; - type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; - type WeightInfo = (); -} - -impl cumulus_pallet_dmp_queue::Config for Runtime { - type Event = Event; - type XcmExecutor = XcmExecutor; - type ExecuteOverweightOrigin = frame_system::EnsureRoot; -} - -pub type LocalAssetTransactor = MultiCurrencyAdapter< - Tokens, - UnknownTokens, - IsNativeConcrete, - AccountId, - LocationToAccountId, - CurrencyId, - CurrencyIdConvert, - DepositToAlternative, ->; - -pub use currency_id_convert::CurrencyIdConvert; - -mod currency_id_convert { - use super::*; - use codec::{Decode, Encode}; - - fn native_currency_location(id: CurrencyId) -> MultiLocation { - MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) - } - - pub struct CurrencyIdConvert; - - impl Convert> for CurrencyIdConvert { - fn convert(id: CurrencyId) -> Option { - match id { - PARENT_CURRENCY_ID => Some(MultiLocation::parent()), - WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), - NATIVE_CURRENCY_ID => Some(native_currency_location(id)), - _ => None, - } - } - } - - impl Convert> for CurrencyIdConvert { - fn convert(location: MultiLocation) -> Option { - fn decode_currency_id(key: Vec) -> Option { - // decode the general key - if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { - // check `currency_id` is cross-chain asset - match currency_id { - WRAPPED_CURRENCY_ID => Some(currency_id), - NATIVE_CURRENCY_ID => Some(currency_id), - _ => None, - } - } else { - None - } - } - - match location { - x if x == MultiLocation::parent() => Some(PARENT_CURRENCY_ID), - MultiLocation { - parents: 1, - interior: X2(Parachain(id), GeneralKey(key)), - } if ParaId::from(id) == ParachainInfo::get() => decode_currency_id(key), - MultiLocation { - // adapt for reanchor canonical location: https://github.com/paritytech/polkadot/pull/4470 - parents: 0, - interior: X1(GeneralKey(key)), - } => decode_currency_id(key), - _ => None, - } - } - } - - impl Convert> for CurrencyIdConvert { - fn convert(asset: MultiAsset) -> Option { - if let MultiAsset { - id: Concrete(location), .. - } = asset - { - Self::convert(location) - } else { - None - } - } - } -} - -parameter_types! { - pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); - pub const MaxAssetsForTransfer: usize = 2; // potentially useful to send both kint and kbtc at once -} - -parameter_type_with_key! { - // Only used for transferring parachain tokens to other parachains using KSM as fee currency. Currently we do not support this, hence return MAX. - // See: https://github.com/open-web3-stack/open-runtime-module-library/blob/cadcc9fb10b8212f92668138fc8f83dc0c53acf5/xtokens/README.md#transfer-multiple-currencies - pub ParachainMinFee: |location: MultiLocation| -> u128 { - #[allow(clippy::match_ref_pats)] // false positive - match (location.parents, location.first_interior()) { - _ => u128::MAX, - } - }; -} - -pub struct AccountIdToMultiLocation; - -impl Convert for AccountIdToMultiLocation { - fn convert(account: AccountId) -> MultiLocation { - X1(AccountId32 { - network: NetworkId::Any, - id: account.into(), - }) - .into() - } -} - -impl orml_xtokens::Config for Runtime { - type Event = Event; - type Balance = Balance; - type CurrencyId = CurrencyId; - type CurrencyIdConvert = CurrencyIdConvert; - type AccountIdToMultiLocation = AccountIdToMultiLocation; - type SelfLocation = SelfLocation; - type XcmExecutor = XcmExecutor; - type Weigher = FixedWeightBounds; - type BaseXcmWeight = UnitWeightCost; - type LocationInverter = ::LocationInverter; - type MaxAssetsForTransfer = MaxAssetsForTransfer; - type MinXcmFee = ParachainMinFee; - type MultiLocationsFilter = Everything; - type ReserveProvider = AbsoluteReserveProvider; -} - impl orml_unknown_tokens::Config for Runtime { type Event = Event; } diff --git a/parachain/runtime/testnet/src/xcm_config.rs b/parachain/runtime/testnet/src/xcm_config.rs new file mode 100644 index 0000000000..61af2b0345 --- /dev/null +++ b/parachain/runtime/testnet/src/xcm_config.rs @@ -0,0 +1,355 @@ +use super::*; +use cumulus_primitives_core::ParaId; +use orml_xcm_support::{DepositToAlternative, IsNativeConcrete, MultiCurrencyAdapter, MultiNativeAsset}; +use pallet_xcm::XcmPassthrough; +use polkadot_parachain::primitives::Sibling; +use xcm::latest::prelude::*; +use xcm_builder::{ + AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, AllowTopLevelPaidExecutionFrom, + EnsureXcmOrigin, FixedRateOfFungible, FixedWeightBounds, LocationInverter, NativeAsset, ParentIsPreset, + RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, + SignedToAccountId32, SovereignSignedViaLocation, TakeRevenue, TakeWeightCredit, +}; +use xcm_executor::{Config, XcmExecutor}; + +parameter_types! { + pub const ParentLocation: MultiLocation = MultiLocation::parent(); + pub const ParentNetwork: NetworkId = NetworkId::Kusama; + pub RelayChainOrigin: Origin = cumulus_pallet_xcm::Origin::Relay.into(); + pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); +} + +/// Means for transacting assets on this chain. +type LocationToAccountId = ( + // The parent (Relay-chain) origin converts to the default `AccountId`. + ParentIsPreset, + // Sibling parachain origins convert to AccountId via the `ParaId::into`. + SiblingParachainConvertsVia, + // Straight up local `AccountId32` origins just alias directly to `AccountId`. + AccountId32Aliases, +); + +/// This is the type we use to convert an (incoming) XCM origin into a local `Origin` instance, +/// ready for dispatching a transaction with Xcm's `Transact`. There is an `OriginKind` which can +/// biases the kind of local `Origin` it will become. +pub type XcmOriginToTransactDispatchOrigin = ( + // Sovereign account converter; this attempts to derive an `AccountId` from the origin location + // using `LocationToAccountId` and then turn that into the usual `Signed` origin. Useful for + // foreign chains who want to have a local sovereign account on this chain which they control. + SovereignSignedViaLocation, + // Native converter for Relay-chain (Parent) location; will converts to a `Relay` origin when + // recognised. + RelayChainAsNative, + // Native converter for sibling Parachains; will convert to a `SiblingPara` origin when + // recognised. + SiblingParachainAsNative, + // Native signed account converter; this just converts an `AccountId32` origin into a normal + // `Origin::Signed` origin of the same 32-byte value. + SignedAccountId32AsNative, + // Xcm origins can be represented natively under the Xcm pallet's Xcm origin. + XcmPassthrough, +); + +parameter_types! { + // One XCM operation is 200_000_000 weight, cross-chain transfer ~= 2x of transfer. + pub UnitWeightCost: Weight = 200_000_000; +} + +pub type Barrier = ( + TakeWeightCredit, + AllowTopLevelPaidExecutionFrom, + AllowKnownQueryResponses, + AllowSubscriptionsFrom, +); // required for others to keep track of our xcm version + +parameter_types! { + pub const MaxInstructions: u32 = 100; +} +pub struct XcmConfig; + +// the ksm cost to to execute a no-op extrinsic +fn base_tx_in_ksm() -> Balance { + KSM.one() / 50_000 +} + +pub fn ksm_per_second() -> u128 { + let base_weight = Balance::from(ExtrinsicBaseWeight::get()); + let base_tx_per_second = (WEIGHT_PER_SECOND as u128) / base_weight; + base_tx_per_second * base_tx_in_ksm() +} + +parameter_types! { + pub KsmPerSecond: (AssetId, u128) = (MultiLocation::parent().into(), ksm_per_second()); + pub KintPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 + MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KINT).encode())), + ).into(), + // KINT:KSM = 4:3 + (ksm_per_second() * 4) / 3 + ); + pub KbtcPerSecond: (AssetId, u128) = ( // can be removed once we no longer need to support polkadot < 0.9.16 + MultiLocation::new( + 1, + X2(Parachain(ParachainInfo::get().into()), GeneralKey(Token(KBTC).encode())), + ).into(), + // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 + ksm_per_second() / 1_500_000 + ); + pub CanonicalizedKintPerSecond: (AssetId, u128) = ( + MultiLocation::new( + 0, + X1(GeneralKey(Token(KINT).encode())), + ).into(), + // KINT:KSM = 4:3 + (ksm_per_second() * 4) / 3 + ); + pub CanonicalizedKbtcPerSecond: (AssetId, u128) = ( + MultiLocation::new( + 0, + X1(GeneralKey(Token(KBTC).encode())), + ).into(), + // KBTC:KSM = 1:150 & Satoshi:Planck = 1:10_000 + ksm_per_second() / 1_500_000 + ); +} + +pub struct ToTreasury; +impl TakeRevenue for ToTreasury { + fn take_revenue(revenue: MultiAsset) { + if let MultiAsset { + id: Concrete(location), + fun: Fungible(amount), + } = revenue + { + if let Some(currency_id) = CurrencyIdConvert::convert(location) { + // Note: we should ensure that treasury account has existential deposit for all of the cross-chain + // asset. Ignore the result. + let _ = Tokens::deposit(currency_id, &TreasuryAccount::get(), amount); + } + } + } +} + +pub type Trader = ( + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, + FixedRateOfFungible, + FreeTestExection, +); + +// If all other trader items fail to apply, then execute for free. This is useful in in xcm +// testing: it allows the sibling to place the assets in UnknownTokens so that it can send +// them back. SHOULD NOT BE INCLUDED IN MAINNET +pub struct FreeTestExection; +impl WeightTrader for FreeTestExection { + fn new() -> Self { + Self + } + + fn buy_weight(&mut self, _weight: Weight, payment: Assets) -> Result { + log::warn!("xcm trader fallthrough: allowing free execution (TESTING ONLY)"); + Ok(payment) + } + + fn refund_weight(&mut self, _weight: Weight) -> Option { + log::warn!("xcm trader refund fallthrough: giving no refund (TESTING ONLY)"); + None + } +} + +impl Config for XcmConfig { + type Call = Call; + type XcmSender = XcmRouter; + // How to withdraw and deposit an asset. + type AssetTransactor = LocalAssetTransactor; + type OriginConverter = XcmOriginToTransactDispatchOrigin; + type IsReserve = MultiNativeAsset; + type IsTeleporter = NativeAsset; // <- should be enough to allow teleportation + type LocationInverter = LocationInverter; + type Barrier = Barrier; + type Weigher = FixedWeightBounds; + type Trader = Trader; + type ResponseHandler = PolkadotXcm; + type SubscriptionService = PolkadotXcm; + type AssetTrap = PolkadotXcm; + type AssetClaims = PolkadotXcm; +} + +/// No local origins on this chain are allowed to dispatch XCM sends/executions. +pub type LocalOriginToLocation = (SignedToAccountId32,); + +/// The means for routing XCM messages which are not for local execution into the right message +/// queues. +pub type XcmRouter = ( + // Two routers - use UMP to communicate with the relay chain: + cumulus_primitives_utility::ParentAsUmp, + // ..and XCMP to communicate with the sibling chains. + XcmpQueue, +); + +impl pallet_xcm::Config for Runtime { + type Event = Event; + type Call = Call; + type Origin = Origin; + type SendXcmOrigin = EnsureXcmOrigin; + type XcmRouter = XcmRouter; + type ExecuteXcmOrigin = EnsureXcmOrigin; + type XcmExecuteFilter = Nothing; + type XcmExecutor = XcmExecutor; + type XcmTeleportFilter = Everything; + type XcmReserveTransferFilter = Everything; + type Weigher = FixedWeightBounds; + type LocationInverter = LocationInverter; + type AdvertisedXcmVersion = pallet_xcm::CurrentXcmVersion; + const VERSION_DISCOVERY_QUEUE_SIZE: u32 = 100; +} + +impl cumulus_pallet_xcm::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; +} + +impl cumulus_pallet_xcmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ChannelInfo = ParachainSystem; + type VersionWrapper = PolkadotXcm; + type ExecuteOverweightOrigin = EnsureRoot; + type ControllerOrigin = EnsureRoot; + type ControllerOriginConverter = XcmOriginToTransactDispatchOrigin; + type WeightInfo = (); +} + +impl cumulus_pallet_dmp_queue::Config for Runtime { + type Event = Event; + type XcmExecutor = XcmExecutor; + type ExecuteOverweightOrigin = frame_system::EnsureRoot; +} + +pub type LocalAssetTransactor = MultiCurrencyAdapter< + Tokens, + UnknownTokens, + IsNativeConcrete, + AccountId, + LocationToAccountId, + CurrencyId, + CurrencyIdConvert, + DepositToAlternative, +>; + +pub use currency_id_convert::CurrencyIdConvert; + +mod currency_id_convert { + use super::*; + use codec::{Decode, Encode}; + + fn native_currency_location(id: CurrencyId) -> MultiLocation { + MultiLocation::new(1, X2(Parachain(ParachainInfo::get().into()), GeneralKey(id.encode()))) + } + + pub struct CurrencyIdConvert; + + impl Convert> for CurrencyIdConvert { + fn convert(id: CurrencyId) -> Option { + match id { + PARENT_CURRENCY_ID => Some(MultiLocation::parent()), + WRAPPED_CURRENCY_ID => Some(native_currency_location(id)), + NATIVE_CURRENCY_ID => Some(native_currency_location(id)), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(location: MultiLocation) -> Option { + fn decode_currency_id(key: Vec) -> Option { + // decode the general key + if let Ok(currency_id) = CurrencyId::decode(&mut &key[..]) { + // check `currency_id` is cross-chain asset + match currency_id { + WRAPPED_CURRENCY_ID => Some(currency_id), + NATIVE_CURRENCY_ID => Some(currency_id), + _ => None, + } + } else { + None + } + } + + match location { + x if x == MultiLocation::parent() => Some(PARENT_CURRENCY_ID), + MultiLocation { + parents: 1, + interior: X2(Parachain(id), GeneralKey(key)), + } if ParaId::from(id) == ParachainInfo::get() => decode_currency_id(key), + MultiLocation { + // adapt for reanchor canonical location: https://github.com/paritytech/polkadot/pull/4470 + parents: 0, + interior: X1(GeneralKey(key)), + } => decode_currency_id(key), + _ => None, + } + } + } + + impl Convert> for CurrencyIdConvert { + fn convert(asset: MultiAsset) -> Option { + if let MultiAsset { + id: Concrete(location), .. + } = asset + { + Self::convert(location) + } else { + None + } + } + } +} + +parameter_types! { + pub SelfLocation: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::get().into()))); + pub const MaxAssetsForTransfer: usize = 2; // potentially useful to send both kint and kbtc at once +} + +parameter_type_with_key! { + // Only used for transferring parachain tokens to other parachains using KSM as fee currency. Currently we do not support this, hence return MAX. + // See: https://github.com/open-web3-stack/open-runtime-module-library/blob/cadcc9fb10b8212f92668138fc8f83dc0c53acf5/xtokens/README.md#transfer-multiple-currencies + pub ParachainMinFee: |location: MultiLocation| -> u128 { + #[allow(clippy::match_ref_pats)] // false positive + match (location.parents, location.first_interior()) { + _ => u128::MAX, + } + }; +} + +pub struct AccountIdToMultiLocation; + +impl Convert for AccountIdToMultiLocation { + fn convert(account: AccountId) -> MultiLocation { + X1(AccountId32 { + network: NetworkId::Any, + id: account.into(), + }) + .into() + } +} + +impl orml_xtokens::Config for Runtime { + type Event = Event; + type Balance = Balance; + type CurrencyId = CurrencyId; + type CurrencyIdConvert = CurrencyIdConvert; + type AccountIdToMultiLocation = AccountIdToMultiLocation; + type SelfLocation = SelfLocation; + type XcmExecutor = XcmExecutor; + type Weigher = FixedWeightBounds; + type BaseXcmWeight = UnitWeightCost; + type LocationInverter = ::LocationInverter; + type MaxAssetsForTransfer = MaxAssetsForTransfer; + type MinXcmFee = ParachainMinFee; + type MultiLocationsFilter = Everything; + type ReserveProvider = AbsoluteReserveProvider; +}