Skip to content

Commit 1e971b8

Browse files
acatangiubkontur
andauthored
pallet-xcm: add new extrinsic for asset transfers using explicit XCM transfer types (#3695)
# Description Add `transfer_assets_using()` for transferring assets from local chain to destination chain using explicit XCM transfer types such as: - `TransferType::LocalReserve`: transfer assets to sovereign account of destination chain and forward a notification XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. - `TransferType::DestinationReserve`: burn local assets and forward a notification to `dest` chain to withdraw the reserve assets from this chain's sovereign account and deposit them to `beneficiary`. - `TransferType::RemoteReserve(reserve)`: burn local assets, forward XCM to `reserve` chain to move reserves from this chain's SA to `dest` chain's SA, and forward another XCM to `dest` to mint and deposit reserve-based assets to `beneficiary`. Typically the remote `reserve` is Asset Hub. - `TransferType::Teleport`: burn local assets and forward XCM to `dest` chain to mint/teleport assets and deposit them to `beneficiary`. By default, an asset's reserve is its origin chain. But sometimes we may want to explicitly use another chain as reserve (as long as allowed by runtime `IsReserve` filter). This is very helpful for transferring assets with multiple configured reserves (such as Asset Hub ForeignAssets), when the transfer strictly depends on the used reserve. E.g. For transferring Foreign Assets over a bridge, Asset Hub must be used as the reserve location. # Example usage scenarios ## Transfer bridged ethereum ERC20-tokenX between ecosystem parachains. ERC20-tokenX is registered on AssetHub as a ForeignAsset by the Polkadot<>Ethereum bridge (Snowbridge). Its asset_id is something like `(parents:2, (GlobalConsensus(Ethereum), Address(tokenX_contract)))`. Its _original_ reserve is Ethereum (only we can't use Ethereum as a reserve in local transfers); but, since tokenX is also registered on AssetHub as a ForeignAsset, we can use AssetHub as a reserve. With this PR we can transfer tokenX from ParaA to ParaB while using AssetHub as a reserve. ## Transfer AssetHub ForeignAssets between parachains AssetA created on ParaA but also registered as foreign asset on Asset Hub. Can use AssetHub as a reserve. And all of the above can be done while still controlling transfer type for `fees` so mixing assets in same transfer is supported. # Tests Added integration tests for showcasing: - transferring local (not bridged) assets from parachain over bridge using local Asset Hub reserve, - transferring foreign assets from parachain to Asset Hub, - transferring foreign assets from Asset Hub to parachain, - transferring foreign assets from parachain to parachain using local Asset Hub reserve. --------- Co-authored-by: Branislav Kontur <[email protected]> Co-authored-by: command-bot <>
1 parent 2dfe5f7 commit 1e971b8

File tree

28 files changed

+2217
-387
lines changed

28 files changed

+2217
-387
lines changed

bridges/modules/xcm-bridge-hub-router/src/lib.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,10 @@ pub mod pallet {
191191
impl<T: Config<I>, I: 'static> Pallet<T, I> {
192192
/// Called when new message is sent (queued to local outbound XCM queue) over the bridge.
193193
pub(crate) fn on_message_sent_to_bridge(message_size: u32) {
194+
log::trace!(
195+
target: LOG_TARGET,
196+
"on_message_sent_to_bridge - message_size: {message_size:?}",
197+
);
194198
let _ = Bridge::<T, I>::try_mutate(|bridge| {
195199
let is_channel_with_bridge_hub_congested = T::WithBridgeHubChannel::is_congested();
196200
let is_bridge_congested = bridge.is_congested;
@@ -238,14 +242,16 @@ impl<T: Config<I>, I: 'static> ExporterFor for Pallet<T, I> {
238242
remote_location: &InteriorLocation,
239243
message: &Xcm<()>,
240244
) -> Option<(Location, Option<Asset>)> {
245+
log::trace!(
246+
target: LOG_TARGET,
247+
"exporter_for - network: {network:?}, remote_location: {remote_location:?}, msg: {message:?}",
248+
);
241249
// ensure that the message is sent to the expected bridged network (if specified).
242250
if let Some(bridged_network) = T::BridgedNetworkId::get() {
243251
if *network != bridged_network {
244252
log::trace!(
245253
target: LOG_TARGET,
246-
"Router with bridged_network_id {:?} does not support bridging to network {:?}!",
247-
bridged_network,
248-
network,
254+
"Router with bridged_network_id {bridged_network:?} does not support bridging to network {network:?}!",
249255
);
250256
return None
251257
}
@@ -300,7 +306,7 @@ impl<T: Config<I>, I: 'static> ExporterFor for Pallet<T, I> {
300306

301307
log::info!(
302308
target: LOG_TARGET,
303-
"Going to send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}",
309+
"Validate send message to {:?} ({} bytes) over bridge. Computed bridge fee {:?} using fee factor {}",
304310
(network, remote_location),
305311
message_size,
306312
fee,
@@ -321,6 +327,7 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> {
321327
dest: &mut Option<Location>,
322328
xcm: &mut Option<Xcm<()>>,
323329
) -> SendResult<Self::Ticket> {
330+
log::trace!(target: LOG_TARGET, "validate - msg: {xcm:?}, destination: {dest:?}");
324331
// `dest` and `xcm` are required here
325332
let dest_ref = dest.as_ref().ok_or(SendError::MissingArgument)?;
326333
let xcm_ref = xcm.as_ref().ok_or(SendError::MissingArgument)?;
@@ -366,6 +373,7 @@ impl<T: Config<I>, I: 'static> SendXcm for Pallet<T, I> {
366373
// increase delivery fee factor if required
367374
Self::on_message_sent_to_bridge(message_size);
368375

376+
log::trace!(target: LOG_TARGET, "deliver - message sent, xcm_hash: {xcm_hash:?}");
369377
Ok(xcm_hash)
370378
}
371379
}

cumulus/pallets/xcmp-queue/src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -942,7 +942,10 @@ impl<T: Config> SendXcm for Pallet<T> {
942942
Self::deposit_event(Event::XcmpMessageSent { message_hash: hash });
943943
Ok(hash)
944944
},
945-
Err(e) => Err(SendError::Transport(e.into())),
945+
Err(e) => {
946+
log::error!(target: LOG_TARGET, "Deliver error: {e:?}");
947+
Err(SendError::Transport(e.into()))
948+
},
946949
}
947950
}
948951
}

cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/genesis.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -80,15 +80,14 @@ pub fn genesis(para_id: u32) -> Storage {
8080
assets: vec![
8181
// Relay Native asset representation
8282
(
83-
Location::try_from(RelayLocation::get()).expect("conversion works"),
83+
Location::try_from(RelayLocation::get()).unwrap(),
8484
PenpalAssetOwner::get(),
8585
true,
8686
ED,
8787
),
8888
// Sufficient AssetHub asset representation
8989
(
90-
Location::try_from(LocalReservableFromAssetHub::get())
91-
.expect("conversion works"),
90+
Location::try_from(LocalReservableFromAssetHub::get()).unwrap(),
9291
PenpalAssetOwner::get(),
9392
true,
9493
ED,

cumulus/parachains/integration-tests/emulated/chains/parachains/testing/penpal/src/lib.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,16 +16,19 @@
1616
mod genesis;
1717
pub use genesis::{genesis, PenpalAssetOwner, PenpalSudoAccount, ED, PARA_ID_A, PARA_ID_B};
1818
pub use penpal_runtime::xcm_config::{
19-
CustomizableAssetFromSystemAssetHub, LocalTeleportableToAssetHub, XcmConfig,
19+
CustomizableAssetFromSystemAssetHub, RelayNetworkId as PenpalRelayNetworkId,
2020
};
2121

2222
// Substrate
2323
use frame_support::traits::OnInitialize;
24+
use sp_core::Encode;
2425

2526
// Cumulus
2627
use emulated_integration_tests_common::{
2728
impl_accounts_helpers_for_parachain, impl_assert_events_helpers_for_parachain,
28-
impl_assets_helpers_for_parachain, impls::Parachain, xcm_emulator::decl_test_parachains,
29+
impl_assets_helpers_for_parachain, impl_xcm_helpers_for_parachain,
30+
impls::{NetworkId, Parachain},
31+
xcm_emulator::decl_test_parachains,
2932
};
3033

3134
// Penpal Parachain declaration
@@ -34,6 +37,10 @@ decl_test_parachains! {
3437
genesis = genesis(PARA_ID_A),
3538
on_init = {
3639
penpal_runtime::AuraExt::on_initialize(1);
40+
frame_support::assert_ok!(penpal_runtime::System::set_storage(
41+
penpal_runtime::RuntimeOrigin::root(),
42+
vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::Rococo.encode())],
43+
));
3744
},
3845
runtime = penpal_runtime,
3946
core = {
@@ -53,6 +60,10 @@ decl_test_parachains! {
5360
genesis = genesis(PARA_ID_B),
5461
on_init = {
5562
penpal_runtime::AuraExt::on_initialize(1);
63+
frame_support::assert_ok!(penpal_runtime::System::set_storage(
64+
penpal_runtime::RuntimeOrigin::root(),
65+
vec![(PenpalRelayNetworkId::key().to_vec(), NetworkId::Westend.encode())],
66+
));
5667
},
5768
runtime = penpal_runtime,
5869
core = {
@@ -77,3 +88,5 @@ impl_assert_events_helpers_for_parachain!(PenpalA);
7788
impl_assert_events_helpers_for_parachain!(PenpalB);
7889
impl_assets_helpers_for_parachain!(PenpalA);
7990
impl_assets_helpers_for_parachain!(PenpalB);
91+
impl_xcm_helpers_for_parachain!(PenpalA);
92+
impl_xcm_helpers_for_parachain!(PenpalB);

cumulus/parachains/integration-tests/emulated/networks/rococo-westend-system/src/lib.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use asset_hub_rococo_emulated_chain::AssetHubRococo;
2525
use asset_hub_westend_emulated_chain::AssetHubWestend;
2626
use bridge_hub_rococo_emulated_chain::BridgeHubRococo;
2727
use bridge_hub_westend_emulated_chain::BridgeHubWestend;
28-
use penpal_emulated_chain::PenpalA;
28+
use penpal_emulated_chain::{PenpalA, PenpalB};
2929
use rococo_emulated_chain::Rococo;
3030
use westend_emulated_chain::Westend;
3131

@@ -48,13 +48,13 @@ decl_test_networks! {
4848
PenpalA,
4949
],
5050
bridge = RococoWestendMockBridge
51-
5251
},
5352
pub struct WestendMockNet {
5453
relay_chain = Westend,
5554
parachains = vec![
5655
AssetHubWestend,
5756
BridgeHubWestend,
57+
PenpalB,
5858
],
5959
bridge = WestendRococoMockBridge
6060
},
@@ -96,5 +96,6 @@ decl_test_sender_receiver_accounts_parameter_types! {
9696
WestendRelay { sender: ALICE, receiver: BOB },
9797
AssetHubWestendPara { sender: ALICE, receiver: BOB },
9898
BridgeHubWestendPara { sender: ALICE, receiver: BOB },
99-
PenpalAPara { sender: ALICE, receiver: BOB }
99+
PenpalAPara { sender: ALICE, receiver: BOB },
100+
PenpalBPara { sender: ALICE, receiver: BOB }
100101
}

cumulus/parachains/integration-tests/emulated/tests/assets/asset-hub-rococo/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ mod imports {
3030
prelude::{AccountId32 as AccountId32Junction, *},
3131
v3,
3232
};
33+
pub use xcm_executor::traits::TransferType;
3334

3435
// Cumulus
3536
pub use asset_test_utils::xcm_helpers;
@@ -81,6 +82,7 @@ mod imports {
8182
pub type SystemParaToParaTest = Test<AssetHubRococo, PenpalA>;
8283
pub type ParaToSystemParaTest = Test<PenpalA, AssetHubRococo>;
8384
pub type ParaToParaThroughRelayTest = Test<PenpalA, PenpalB, Rococo>;
85+
pub type ParaToParaThroughAHTest = Test<PenpalA, PenpalB, AssetHubRococo>;
8486
}
8587

8688
#[cfg(test)]

0 commit comments

Comments
 (0)