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

feat: enable bridged reserves from AssetHub #784

Merged
merged 18 commits into from
May 20, 2024

Conversation

dmoka
Copy link
Contributor

@dmoka dmoka commented Mar 12, 2024

Description

Assets bridged from other consensus systems using the new Bridge Hub system parachain are held in reserve in Asset Hub. Asset Hub contains a second instance of the pallet-assets called the foreignAssets where bridged assets are registered. Foreign assets have a parents field > 1.

Also adding reserve support for DOT coming from AssetHub.

Related Issue

Motivation and Context

Make swaps and pools using foreign assets on HydraDX, specifically assets from Snowbridge. https://github.com/Snowfork/snowbridge

How Has This Been Tested?

TODO: Add a new integration test to integration-tests/src/cross_chain_transfer.rs which tests receiving a foreign asset from assethub.

Checklist:

  • I have updated the documentation if necessary.
  • I have added tests to cover my changes, regression test if fixing an issue.

@dmoka dmoka self-assigned this Mar 12, 2024
Copy link

github-actions bot commented Mar 12, 2024

Crate versions that have been updated:

  • runtime-integration-tests: v1.21.4 -> v1.21.5
  • hydradx-runtime: v234.0.0 -> v235.0.0

Runtime version has been increased.

Copy link
Contributor

@alistair-singh alistair-singh left a comment

Choose a reason for hiding this comment

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

Thanks for picking up this PR. I left some comments about the specifics of the XCM program we are sending.

//ReserveAssetDeposited(assets.clone()),
BuyExecution { fees, weight_limit },
DepositAsset {
assets: Wild(AllCounted(max_assets)),
Copy link
Contributor

Choose a reason for hiding this comment

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

Here is the exact xcm we send. https://github.com/Snowfork/polkadot-sdk/blob/snowbridge/bridges/snowbridge/primitives/router/src/inbound/mod.rs#L259-L271

  1. We reserve deposit both DOT and asset being transferred. We always send both DOT and the asset together in one xcm program.
  2. Buy execution with DOT only.
  3. Deposit the transfer asset only.

Copy link
Contributor Author

@dmoka dmoka Mar 14, 2024

Choose a reason for hiding this comment

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

Thanks for the clarification @alistair-singh .

I am planning to send this message then:

	// executed on local (AssetHub)
	let message = Xcm(vec![
		WithdrawAsset(vec![dot_as_fee.clone(), assets.clone().into()].into()),
		DepositReserveAsset {
			assets: Definite(vec![dot_as_fee.clone(), assets.clone().into()].into()),
			dest,
			xcm: Xcm(vec![
				// executed on remote (on hydra)
				BuyExecution {
					fees: dot_as_fee,
					weight_limit,
				},
				DepositAsset {
					assets: Definite(assets.into()),
					beneficiary,
				},
			]),
		},
	]);

Do you think it represents your usecase? At first I really need to do a WithdrawAsset, otherwise I have nothing in the holding.

Copy link
Contributor

Choose a reason for hiding this comment

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

This looks correct and supports our case.

WithdrawAsset is one way to get assets in holding, but then you would need to create the asset, mint it and send some to a user before you execute the message as that user. Another way you can get assets into holding is by abusing the ReceiveTeleportedAsset and ReserveAssetDeposited instructions to force the assets into holding.


let max_assets = assets.len() as u32 + 1;
let context = X2(GlobalConsensus(NetworkId::Polkadot), Parachain(ACALA_PARA_ID));
let fees = assets
Copy link
Contributor

Choose a reason for hiding this comment

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

Fee will always be DOT. We always buy execution with DOT.

Comment on lines 655 to 662
let foreign_asset: MultiAssets = MultiAsset::from((
MultiLocation {
parents: 1,
interior: Junctions::Here,
},
100 * UNITS,
))
.into();
Copy link
Contributor

Choose a reason for hiding this comment

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

We will never transfer DOT on its own. We will always transfer DOT and the asset selected by the user. The DOT is only there to pay for execution.

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 is a different use case from the foreign asset case, here we want to allow AssetHub to send DOT directly.

So we just implemented it in the same PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Maybe @green-jay can hop in here, as he requested this feature from me.

Copy link
Contributor

@alistair-singh alistair-singh left a comment

Choose a reason for hiding this comment

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

Looks good!

@green-jay
Copy link
Contributor

@dmoka can you please add test of sending DOT from HydraDX to Asset Hub?

@jak-pan
Copy link
Contributor

jak-pan commented Apr 9, 2024

Can we pls remove the DOT<>AH reserve chain and merge the original PR?

@dmoka
Copy link
Contributor Author

dmoka commented Apr 9, 2024

@alistair-singh After some discussion with our team members, we realized that we don't want to allow DOT reserve deposited in our chain because of security concerns.

We still want to accept foreign assets tho. In your code you said you would reserve deposit the foreign asset together with DOT as fee, so it would not be the best for us. What do you think, would it be possible do the same thing on your side but without reserve depositing DOT?

@alistair-singh
Copy link
Contributor

We still want to accept foreign assets tho. In your code you said you would reserve deposit the foreign asset together with DOT as fee, so it would not be the best for us. What do you think, would it be possible do the same thing on your side but without reserve depositing DOT?

Hey @dmoka, The issue is that we need to pay for execution some how and as a bridge we have three options to pay for execution on the destination parachain (HydraDX in this case is the destination).

  1. Pay for execution with DOT.
  2. Pay for execution with the Asset that is being transferred via ReserveDeposit. Requires a Trader on your xcm-executor?
  3. Allow unpaid execution from AssetHub. (Probably not an option)

So we currently chose DOT as it is the only thing we can guarantee most chains will accept for payment. Is the issue that we are using ReserveDepositAsset for DOT instead of Teleporting DOT?

@alistair-singh
Copy link
Contributor

@dmoka To help my understanding of the core issue, why do we consider allowing DOT as a reserve a security risk?

I also want to note that the DOT being transferred here is not a bridged or wrapped version of DOT from Ethereum. It is from Asset Hub's sovereign account on the Bridge Hub parachain. This account and its funds do not leave the security domain provided by the relaychain and only exists to pay bridge-related fees. It will be topped up by governance regularly from the treasury so it is a closed loop.

@jak-pan
Copy link
Contributor

jak-pan commented Apr 19, 2024

To summarize also offchain conversations,

...we can't currently allow AH to be DOT reserve because it breaks reserve backed transfer accounting. 
Imagine Alice sending 1000 DOT from Polkadot to HydraDX and then trying to withdraw to AH. 
She can't because our parachain accont doesn't have 1000DOT. 
Teleporting is not an option as AH + Polkadot would need to trust us as reserve chain.

You can only send DOT through it's reserve chain currently which is Polkadot,
and I guess this will be the case for all of the parachains

So the only viable option ATM is to get some asset that is supported (not DOT) on AH and transfer this with the message.
I guess we accept most of the common assets (all of the Omnipool assets) as fee payment asset.

The proposed solution for now is to use the transported asset as fee payment asset. For this to work we need to register this asset as sufficient / fee payment asset.

For other assets we can still support them but it would need to end at AH and users would need to transport to hydra from there using some other sufficient asset as fee.

Alternatively use assetConversion on AH to change to any compatible asset to pay the fees and ED. i.e. HDX

How should we proceed?

@alistair-singh
Copy link
Contributor

How should we proceed?

Since you cannot allow Asset Hub as a reserve we cannot use our "Forward assets to destination parachain" functionality as it requires Asset Hub to be the reserve for DOT. We will probably patch this feature in the future to accept either the destination chain's native asset or the asset being transferred.

This means now that users must do 2 transfers to get assets to Hydra. One transfer from Ethereum to Asset Hub using the bridge. The second transfer using pallet-xcm to transfer the asset from Asset Hub to Hydra.

With that in mind, I think from the perspective of this PR we need to support the following cases for Users:

  1. Using the asset being transferred as fee if it is sufficient.
  2. Using your native asset (Hydra) as fee.
  3. Any existing asset that is sufficient (USDT?) as fee.

Are all these cases supported from Asset Hub?

@jak-pan
Copy link
Contributor

jak-pan commented Apr 23, 2024

I have chimed in the discussion and we'll check these cases but they should be supported.

@dmoka
Copy link
Contributor Author

dmoka commented Apr 26, 2024

So for now as the discussions are ongoing and might take for a while, we are going to get rid of the support of accepting DOT as reserve asset.

And going to merge the PR with just accepting foreign currencies.

@alistair-singh Let me know if you have objection on this.

&& matches!(
asset,
MultiAsset {
id: Concrete(MultiLocation { parents: 2, .. }),
Copy link
Contributor

@alistair-singh alistair-singh Apr 29, 2024

Choose a reason for hiding this comment

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

This filter is potentially too open. Maybe checking that the interior of the multi-location starts with GlobalConsensus(Ethereum{..}) and matches any chain_id, rather than accept any asset with Parents > 1. This will limit the assets to Snowbridge assets as opposed to any asset. But this is just a suggestion.

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think we really have issue with accepting any asset. You'll need to pay fees anyway and we allow to permissionlessly register any token for users already. Unless there can be some way to exploit this on xcm level...

Do you see any potential risks from top of your head?

cc @mrq1911

@alistair-singh
Copy link
Contributor

Let me know if you have objection on this.

No objections. I think this is the right way forward for now.

@green-jay
Copy link
Contributor

LGTM

runtime/hydradx/src/xcm.rs Outdated Show resolved Hide resolved
Copy link
Contributor

@alistair-singh alistair-singh left a comment

Choose a reason for hiding this comment

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

LGTM

@dmoka dmoka merged commit e747819 into master May 20, 2024
4 checks passed
@dmoka dmoka deleted the alistair/enable-bridged-reserves branch May 20, 2024 13:41
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants