Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

FRAME: Unity Balance Conversion for Different IDs of Native Asset #3659

Merged
merged 12 commits into from
Apr 16, 2024

Conversation

muharem
Copy link
Contributor

@muharem muharem commented Mar 12, 2024

Introduce types to define 1:1 balance conversion for different relative asset ids/locations of native asset.

Examples:
native asset on Asset Hub presented as VersionedLocatableAsset type in the context of Relay Chain is

{
  `location`: (0, Parachain(1000)),
  `asset_id`: (1, Here),
}

and it's balance should be converted 1:1 by implementations of ConversionToAssetBalance trait.

@muharem muharem added the T1-FRAME This PR/Issue is related to core FRAME, the framework. label Mar 12, 2024
@muharem muharem requested review from a team as code owners March 12, 2024 12:01
Copy link
Contributor

@bkontur bkontur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgmt, just two nits and I didn't check integration tests, just runtimes and xcm stuff

polkadot/runtime/common/src/impls.rs Outdated Show resolved Hide resolved
Comment on lines 154 to 157
/// Adapter for [`Contains`] trait to match [`VersionedLocatableAsset`] type converted to the latest
/// version of itself where it's location matched by `L` and it's asset id by `A` parameter types.
pub struct ContainsLatest<L, A>(PhantomData<(L, A)>);
impl<L, A> Contains<VersionedLocatableAsset> for ContainsLatest<L, A>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it's implemented over VersionedLocatableAsset type, which is part of common.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, I thought VersionedLocatableAsset was part of the XCM stuff, so maybe we could move everything related to VersionedLocatableAsset and ContainsLatest to one place in the XCM builder., e.g.:
https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/xcm-builder/src/locatable_asset.rs.
(I think I saw some complaints about adding too much stuff to the polkadot-runtime-common.)

As I see, we already have LocatableAssetId in xcm-builder here: https://github.com/paritytech/polkadot-sdk/blob/master/polkadot/xcm/xcm-builder/src/pay.rs#L191-L198, which could possibly also go to the locatable_asset.rs

And maybe VersionedLocatableAsset could also wrap that LocatableAssetId, but I think this can be just from V5, e.g.:

pub enum VersionedLocatableAsset {
...
	#[codec(index = 4)]
-        V4 { location: xcm::v4::Location, asset_id: xcm::v4::AssetId },
+	V4(LocatableAssetId),
}

Copy link
Contributor Author

@muharem muharem Mar 25, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wont wrap use LocatableAssetId directly for the last version from VersionedLocatableAsset. It will be confusion, different version will look different.

For now, I will keep everything as it is, specially wont change it with this PR.
Every time someone see these types, ( VersionedLocatableAsset and LocatableAssetId), they suggest to remove it and use only Location, refactor it somehow, etc. Since we should be more conservative with xcm-builder, I would not move it for now.

cc: @franciscoaguirre

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we should be more conservative with xcm-builder...

Where is this coming from? Can you elaborate more?

Every time someone see these types, ( VersionedLocatableAsset and LocatableAssetId), they suggest to remove it and use only Location, refactor it somehow, etc.

Well, perhaps it's worth considering refactoring it then.

I think keeping VersionedLocatableAsset outside of xcm / xcm-builder, where we currently have all Versioned* stuff and also LocatableAssetId, seems kind of strange. As far as I can see, it is used by relay runtimes and collectives runtime, so from this perspective, polkadot-runtime-common is not the best place (xcm-builder seems better):

[package]
name = "polkadot-runtime-common"
description = "Pallets and constants used in Relay Chain networks."

VersionedLocatableAsset contains v3 and v4. How do you propose keeping this up-to-date? Do you expect that when we add xcm::v5, someone will also update this? If so, there's no way to know from the xcm module's point of view (considering that one day we might move xcm out of polkadot-sdk) that some versioned stuff is used elsewhere. If not, for example, when we add xcm::v6, we remove xcm:v3. Is that okay? I think the check-migrations jobs should detect this and fail.

Another thing to consider, I see it is used for pallet_collective as the key for ConversionRateToNative or pallet_treasury as AssetKind. I think it's a similar situation to what's discussed here: #1130. And if I recall correctly, the last time we spoke with Joe about pallet_assets and Location as AssetId, everything pointed to the solution of using xcm::latest::prelude::Location as AssetId (with migrations) and VersionedLocation as AssetIdParameter (which is used for extrinsics).

I think VersionedLocatableAsset presents a very similar situation. Perhaps it's worth considering splitting AssetKind into AssetKind / AssetKindParameter. For example, this could help avoid using Box for extrinsic parameters like asset_kind: Box<T::AssetKind>, though I'm not sure if this is good or bad, it can be left for the runtime to decide. My point is that ideally, we should have one method/pattern for storing XCM-related data.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will check that draft PR, and I am also looking for a solution where that PayOverXcm can also detect that the asset is located on local chan, but beneficiary is on Asset Hub. We have this use case when treasury wants to fund it's own account on Asset Hub. I will initiate a discussion around it, and open an RFC.
@franciscoaguirre please approve this PR if you happy with it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only want to understand the purpose of this change. Is it because DOT was being passed on to the asset rate converter? Or is it to allow things like USDT to be used 1:1 when they come from system parachains?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, with sovereign accounts it definitely becomes harder. My draft PR was of the idea that we could just use the beneficiary location to know to which chain to send it to. It wouldn't work when we want to send it somewhere different than the location to fund a sovereign account.

We might need then a location for the chain and another for the beneficiary, like on the send extrinsic in pallet-xcm. There's no need to name it locatable asset though, just a regular asset, a regular destination, and a regular beneficiary.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I only want to understand the purpose of this change. Is it because DOT was being passed on to the asset rate converter? Or is it to allow things like USDT to be used 1:1 when they come from system parachains?

The asset rate does not know there are different locations of native asset (as in the example in the PR description). And will require a rate to be set for it. With there types we make asset conversion understand different locations of native asset, and do not require a rate to be set for it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that's one of the problems of marking the difference between the same asset on different chains

muharem and others added 2 commits March 20, 2024 15:32
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
fellowship-merge-bot bot pushed a commit to polkadot-fellows/runtimes that referenced this pull request Mar 24, 2024
Treasury Spend detects the relative locations of native asset to compare
origin's spend limits.

When a spend is approved for a native asset located on Asset Hub, the
AssetRate pallet instance does not recognize the location (parent: 1,
here) as a native asset. Therefore, it requires the asset rate to be set
in advance to the rate of `1`. To address this issue in this PR, we
introduce new types to wrap the `AssetRate` implementation of balance
conversion. These types match the native asset's relative locations and
result in a 1:1 balance conversion in such cases.

In addition we provide integration tests to cover these cases.

Examples:
Treasury pallet on Relay Chain accepts the spend of the native asset on
Asset Hub with it's ID presented as `VersionedLocatableAsset` type
```
{
  `location`: (0, Parachain(1000)),
  `asset_id`: (1, Here),
}
```
and it's balance should be converted 1:1 by treasury pallet's
`BalanceConverter` type.

Note: I plan to introduce a similar but configurable implementation in
the polkadot-sdk and utilize it here. However, to get this change in the
next release, I open this PR first.

Frame version: paritytech/polkadot-sdk#3659

---------

Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com>
Co-authored-by: Branislav Kontur <bkontur@gmail.com>
Co-authored-by: Bastian Köcher <git@kchr.de>
@github-actions github-actions bot requested a review from bkontur April 15, 2024 15:29
Copy link

Review required! Latest push from author must always be reviewed

Comment on lines +112 to +113
location: asset_hub_location.clone(),
asset_id: native_asset.into(),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird how the location is relative to the relay chain but the asset id is relative to asset hub. Is this correct?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, this is correct. DOT is parent on asset hub

assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::Balances(pallet_balances::Event::Transfer { .. }) => {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could specify the beneficiary here, so we know the transfer is working

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this assert is broken, I cannot really match beneficiary here. but we are fine here, I check the balance above

assert_expected_events!(
Rococo,
vec![
RuntimeEvent::Treasury(pallet_treasury::Event::Paid { .. }) => {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the beneficiary of this can be asserted as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not work right now with this macro

assert_expected_events!(
Westend,
vec![
RuntimeEvent::XcmPallet(pallet_xcm::Event::Sent { .. }) => {},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could assert the destination

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It does not work right now with this macro

Comment on lines +101 to +102
let fellowship_treasury_location: Location =
Location::new(1, [Parachain(1001), PalletInstance(65)]);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the sovereign account in Asset Hub of the fellowship treasury?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Comment on lines +325 to +347
/// Matches if the given location is a system-level sibling parachain.
pub struct IsSiblingSystemParachain<ParaId, SelfParaId>(PhantomData<(ParaId, SelfParaId)>);
impl<ParaId: IsSystem + From<u32> + Eq, SelfParaId: Get<ParaId>> Contains<Location>
for IsSiblingSystemParachain<ParaId, SelfParaId>
{
fn contains(l: &Location) -> bool {
matches!(
l.unpack(),
(1, [Junction::Parachain(id)])
if SelfParaId::get() != ParaId::from(*id) && ParaId::from(*id).is_system(),
)
}
}

/// Matches if the given location contains only the specified amount of parents and no interior
/// junctions.
pub struct IsParentsOnly<Count>(PhantomData<Count>);
impl<Count: Get<u8>> Contains<Location> for IsParentsOnly<Count> {
fn contains(t: &Location) -> bool {
t.contains_parents_only(Count::get())
}
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This doesn't look like it should go in barriers.rs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

its consistent to what we have in this file, IsChildSystemParachain just above for example

/// Implements [`ConversionFromAssetBalance`], allowing for a 1:1 balance conversion of the asset
/// when it meets the conditions specified by `C`. If the conditions are not met, the conversion is
/// delegated to `O`.
pub struct UnityOrOuterConversion<C, O>(core::marker::PhantomData<(C, O)>);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub struct UnityOrOuterConversion<C, O>(core::marker::PhantomData<(C, O)>);
pub struct OneToOneIfElse<C, O>(core::marker::PhantomData<(C, O)>);

or

Suggested change
pub struct UnityOrOuterConversion<C, O>(core::marker::PhantomData<(C, O)>);
pub struct OneToOneOrOuterConversion<C, O>(core::marker::PhantomData<(C, O)>);

I think it expresses what you want better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we have type above with a name - UnityAssetBalanceConversion, which also means 1:1. its not wrong and I would keep it consistent.

doc:
- audience: Runtime Dev
description: |
Introduce types to define 1:1 balance conversion for different relative asset ids/locations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the purpose of this change? Should probably mention here to the users what has now changed in how the system is used.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not sure why you mean, this does not changes anything for users, only introduces few new types.
it does changes configuration for westend and rococo chains, but it changes it a way it is expected to work

Copy link
Contributor

@bkontur bkontur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

one nit, I suggest renaming ContainsLatest to the ContainsAssetFrom

@muharem muharem added this pull request to the merge queue Apr 16, 2024
Merged via the queue into master with commit 6f3d890 Apr 16, 2024
133 of 136 checks passed
@muharem muharem deleted the muharem-unity-balance-conversion branch April 16, 2024 16:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T1-FRAME This PR/Issue is related to core FRAME, the framework.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants