-
Notifications
You must be signed in to change notification settings - Fork 378
support asset transfer from/to sibling chain #806
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,9 +22,10 @@ use frame_support::traits::{ | |
}; | ||
use pallet_asset_tx_payment::HandleCredit; | ||
use sp_runtime::traits::Zero; | ||
use sp_std::marker::PhantomData; | ||
use xcm::latest::{AssetId, Fungibility::Fungible, MultiAsset, MultiLocation}; | ||
use xcm_executor::traits::FilterAssetLocation; | ||
use sp_std::{borrow::Borrow, marker::PhantomData, result}; | ||
use xcm::latest::{AssetId, Fungibility::Fungible, Junction, MultiAsset, MultiLocation}; | ||
|
||
use xcm_executor::traits::{Convert, FilterAssetLocation}; | ||
|
||
/// Type alias to conveniently refer to the `Currency::NegativeImbalance` associated type. | ||
pub type NegativeImbalance<T> = <pallet_balances::Pallet<T> as Currency< | ||
|
@@ -113,6 +114,44 @@ impl<T: Get<MultiLocation>> FilterAssetLocation for AssetsFrom<T> { | |
} | ||
} | ||
|
||
/// Converter struct with two main usage scenarios: | ||
/// 1. Transfer asset from local to other para-chain | ||
/// - with `reverse_ref` to convert a numeric asset ID (must be `TryFrom/TryInto<u128>`) into a `GeneralIndex` junction | ||
/// 2. Transfer asset back from other para-chain to local | ||
/// - with `convert_ref` to convert multilocation struct `(1,X2(ParaChain(para_id),GeneralIndex(general_index))` to a numeric asset ID | ||
pub struct AsPrefixedGeneralIndexFromLocalOrRemote< | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wrong place for this - it should be in xcm-builder. |
||
LocalPrefix, | ||
RemotePrefix, | ||
AssetId, | ||
ConvertAssetId, | ||
>(PhantomData<(LocalPrefix, RemotePrefix, AssetId, ConvertAssetId)>); | ||
impl< | ||
LocalPrefix: Get<MultiLocation>, | ||
RemotePrefix: Get<MultiLocation>, | ||
AssetId: Clone, | ||
ConvertAssetId: Convert<u128, AssetId>, | ||
> Convert<MultiLocation, AssetId> | ||
for AsPrefixedGeneralIndexFromLocalOrRemote<LocalPrefix, RemotePrefix, AssetId, ConvertAssetId> | ||
{ | ||
fn convert_ref(id: impl Borrow<MultiLocation>) -> result::Result<AssetId, ()> { | ||
let id = id.borrow(); | ||
|
||
match id.match_and_split(&LocalPrefix::get()) { | ||
Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert_ref(id), | ||
_ => match id.match_and_split(&RemotePrefix::get()) { | ||
Some(Junction::GeneralIndex(id)) => ConvertAssetId::convert_ref(id), | ||
_ => Err(()), | ||
}, | ||
} | ||
} | ||
fn reverse_ref(what: impl Borrow<AssetId>) -> result::Result<MultiLocation, ()> { | ||
let mut location = LocalPrefix::get(); | ||
let id = ConvertAssetId::reverse_ref(what)?; | ||
location.push_interior(Junction::GeneralIndex(id)).map_err(|_| ())?; | ||
Ok(location) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
@@ -130,7 +169,9 @@ mod tests { | |
traits::{BlakeTwo256, IdentityLookup}, | ||
Perbill, | ||
}; | ||
|
||
use xcm::prelude::*; | ||
use xcm_executor::traits::JustTry; | ||
|
||
type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic<Test>; | ||
type Block = frame_system::mocking::MockBlock<Test>; | ||
|
@@ -281,4 +322,59 @@ mod tests { | |
"AssetsFrom should allow assets from any of its interior locations" | ||
); | ||
} | ||
|
||
#[test] | ||
fn assets_from_convert_correctly() { | ||
parameter_types! { | ||
pub const Local: MultiLocation = Here.into(); | ||
pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(1000))); | ||
} | ||
let local_asset_location = Local::get().pushed_with_interior(GeneralIndex(42)).unwrap(); | ||
let remote_asset_location = Remote::get().pushed_with_interior(GeneralIndex(42)).unwrap(); | ||
let mut asset_id = | ||
AsPrefixedGeneralIndexFromLocalOrRemote::<Local, Remote, u32, JustTry>::convert_ref( | ||
&local_asset_location, | ||
) | ||
.unwrap_or(u32::default()); | ||
assert_eq!(asset_id, 42); | ||
asset_id = | ||
AsPrefixedGeneralIndexFromLocalOrRemote::<Local, Remote, u32, JustTry>::convert_ref( | ||
&remote_asset_location, | ||
) | ||
.unwrap_or(u32::default()); | ||
assert_eq!(asset_id, 42); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously I have tried to summarize your problem as the following:
But this unit test seems to indicate that you're doing something more -- you're trying to represent a remote asset containing a If so, then this is absolutely not the right thing to do. Assets should always have ONE canonical There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Pondering a bit more into this, I think what you are trying to solve is the reanchoring issue, in which case, this is an operation that should have been done by the XCM sender, and not the recipient, i.e. when sending an asset containing a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @KiChjang @apopiak I do not think we can achive that without hack in cumulus, let us put more details here:
since
I agree with
so I think from the perspective of our chain statemine asset should always reprensented as There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. A couple of observations:
I've looked into the code for the XCM executor now and it does seem like there's no way to burn and convert a derivative asset back to its reserve asset during the execution of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
No, I do think we should use reserve asset rather than derivative asset here. except for statemine we need to talk to other parachains like Acala and as you mentioned before only the GeneralIndex is too abstract. How to differ the same reprensentation There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@KiChjang I really do not know this primitive and will try, thanks for your suggestion There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @KiChjang There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is purposefully not implemented; as I said, we do not provide any opinionated approaches as to how a chain should manage their bookkeeping of their reserve assets. Any chain that wants to execute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Understood. we can implement this to do some asset convert logic from sender side. But I would assume at last we still need There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct, the reanchoring logic is what we're in the middle of investigating as well. However, in the context of this PR, your changes will unfortunately not be the correct fix. We will provide an update in #827 as soon as we have any more details to share. |
||
} | ||
|
||
#[test] | ||
fn assets_from_convert_error_with_different_parent() { | ||
parameter_types! { | ||
pub const Local: MultiLocation = Here.into(); | ||
pub Remote: MultiLocation = MultiLocation::new(2, Junctions::Here); | ||
} | ||
let remote_asset_location = MultiLocation::new(1, X1(GeneralIndex(42))); | ||
let asset_id = | ||
AsPrefixedGeneralIndexFromLocalOrRemote::<Local, Remote, u32, JustTry>::convert_ref( | ||
&remote_asset_location, | ||
) | ||
.unwrap_or(u32::default()); | ||
assert_eq!(asset_id, u32::default()) | ||
} | ||
|
||
#[test] | ||
fn assets_from_convert_error_with_different_interiors() { | ||
parameter_types! { | ||
pub const Local: MultiLocation = Here.into(); | ||
pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(1000))); | ||
} | ||
let mut remote_asset_location = | ||
Remote::get().pushed_with_interior(PalletInstance(10)).unwrap(); | ||
remote_asset_location = | ||
remote_asset_location.pushed_with_interior(GeneralIndex(42)).unwrap(); | ||
let asset_id = | ||
AsPrefixedGeneralIndexFromLocalOrRemote::<Local, Remote, u32, JustTry>::convert_ref( | ||
&remote_asset_location, | ||
) | ||
.unwrap_or(u32::default()); | ||
assert_eq!(asset_id, u32::default()) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -65,17 +65,18 @@ use parachains_common::{ | |
pub use sp_runtime::BuildStorage; | ||
|
||
// Polkadot imports | ||
use crate::common::impls::AsPrefixedGeneralIndexFromLocalOrRemote; | ||
use pallet_xcm::{EnsureXcm, IsMajorityOfBody, XcmPassthrough}; | ||
use polkadot_parachain::primitives::Sibling; | ||
use polkadot_runtime_common::{BlockHashCount, RocksDbWeight, SlowAdjustingFeeUpdate}; | ||
use xcm::latest::prelude::*; | ||
use xcm_builder::{ | ||
AccountId32Aliases, AllowKnownQueryResponses, AllowSubscriptionsFrom, | ||
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, AsPrefixedGeneralIndex, | ||
ConvertedConcreteAssetId, CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, | ||
FungiblesAdapter, IsConcrete, LocationInverter, NativeAsset, ParentAsSuperuser, | ||
ParentIsDefault, RelayChainAsNative, SiblingParachainAsNative, SiblingParachainConvertsVia, | ||
SignedAccountId32AsNative, SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, | ||
AllowTopLevelPaidExecutionFrom, AllowUnpaidExecutionFrom, ConvertedConcreteAssetId, | ||
CurrencyAdapter, EnsureXcmOrigin, FixedWeightBounds, FungiblesAdapter, IsConcrete, | ||
LocationInverter, NativeAsset, ParentAsSuperuser, ParentIsDefault, RelayChainAsNative, | ||
SiblingParachainAsNative, SiblingParachainConvertsVia, SignedAccountId32AsNative, | ||
SovereignSignedViaLocation, TakeWeightCredit, UsingComponents, | ||
}; | ||
use xcm_executor::{traits::JustTry, Config, XcmExecutor}; | ||
|
||
|
@@ -445,6 +446,7 @@ parameter_types! { | |
pub Ancestry: MultiLocation = Parachain(ParachainInfo::parachain_id().into()).into(); | ||
pub const Local: MultiLocation = Here.into(); | ||
pub CheckingAccount: AccountId = PolkadotXcm::check_account(); | ||
pub Remote: MultiLocation = MultiLocation::new(1, X1(Parachain(ParachainInfo::parachain_id().into()))); | ||
} | ||
|
||
/// Type for specifying how a `MultiLocation` can be converted into an `AccountId`. This is used | ||
|
@@ -481,7 +483,7 @@ pub type FungiblesTransactor = FungiblesAdapter< | |
ConvertedConcreteAssetId< | ||
AssetId, | ||
Balance, | ||
AsPrefixedGeneralIndex<Local, AssetId, JustTry>, | ||
AsPrefixedGeneralIndexFromLocalOrRemote<Local, Remote, AssetId, JustTry>, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No alterations to Statemint's XCM right now. Stuff like this is EXTREMELY dangerous to change and must a) be completely understood by me; and b) be in place in Kusama for some time prior. |
||
JustTry, | ||
>, | ||
// Convert an XCM MultiLocation into a local account id: | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Documentation needs improvement. This is incomprehensible at present.