Skip to content

Commit d893cde

Browse files
yrongclaravanstadenacatangiu
authored
Snowbridge: deposit extra fee to beneficiary on Asset Hub (#4175)
Just the upper-stream for Snowfork#137 and more context there. --------- Co-authored-by: Clara van Staden <claravanstaden64@gmail.com> Co-authored-by: Adrian Catangiu <adrian@parity.io>
1 parent 988e30f commit d893cde

3 files changed

Lines changed: 149 additions & 3 deletions

File tree

  • bridges/snowbridge/primitives/router/src/inbound
  • cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests
  • prdoc

bridges/snowbridge/primitives/router/src/inbound/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -273,8 +273,10 @@ where
273273
},
274274
None => {
275275
instructions.extend(vec![
276-
// Deposit asset to beneficiary.
277-
DepositAsset { assets: Definite(asset.into()), beneficiary },
276+
// Deposit both asset and fees to beneficiary so the fees will not get
277+
// trapped. Another benefit is when fees left more than ED on AssetHub could be
278+
// used to create the beneficiary account in case it does not exist.
279+
DepositAsset { assets: Wild(AllCounted(2)), beneficiary },
278280
]);
279281
},
280282
}

cumulus/parachains/integration-tests/emulated/tests/bridges/bridge-hub-rococo/src/tests/snowbridge.rs

Lines changed: 132 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use snowbridge_pallet_inbound_queue_fixtures::{
2727
};
2828
use snowbridge_pallet_system;
2929
use snowbridge_router_primitives::inbound::{
30-
Command, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
30+
Command, Destination, GlobalConsensusEthereumConvertsFor, MessageV1, VersionedMessage,
3131
};
3232
use sp_core::H256;
3333
use sp_runtime::{DispatchError::Token, TokenError::FundsUnavailable};
@@ -40,6 +40,7 @@ const TREASURY_ACCOUNT: [u8; 32] =
4040
const WETH: [u8; 20] = hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d");
4141
const ETHEREUM_DESTINATION_ADDRESS: [u8; 20] = hex!("44a57ee2f2FCcb85FDa2B0B18EBD0D8D2333700e");
4242
const INSUFFICIENT_XCM_FEE: u128 = 1000;
43+
const XCM_FEE: u128 = 4_000_000_000;
4344

4445
#[derive(Encode, Decode, Debug, PartialEq, Eq, Clone, TypeInfo)]
4546
pub enum ControlCall {
@@ -555,3 +556,133 @@ fn register_weth_token_in_asset_hub_fail_for_insufficient_fee() {
555556
);
556557
});
557558
}
559+
560+
fn send_token_from_ethereum_to_asset_hub_with_fee(account_id: [u8; 32], fee: u128) {
561+
let weth_asset_location: Location = Location::new(
562+
2,
563+
[EthereumNetwork::get().into(), AccountKey20 { network: None, key: WETH }],
564+
);
565+
// (Parent, Parent, EthereumNetwork::get(), AccountKey20 { network: None, key: WETH })
566+
// Fund asset hub sovereign on bridge hub
567+
let asset_hub_sovereign = BridgeHubRococo::sovereign_account_id_of(Location::new(
568+
1,
569+
[Parachain(AssetHubRococo::para_id().into())],
570+
));
571+
BridgeHubRococo::fund_accounts(vec![(asset_hub_sovereign.clone(), INITIAL_FUND)]);
572+
573+
// Register WETH
574+
AssetHubRococo::execute_with(|| {
575+
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;
576+
577+
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::force_create(
578+
RuntimeOrigin::root(),
579+
weth_asset_location.clone().try_into().unwrap(),
580+
asset_hub_sovereign.into(),
581+
false,
582+
1,
583+
));
584+
585+
assert!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::asset_exists(
586+
weth_asset_location.clone().try_into().unwrap(),
587+
));
588+
});
589+
590+
// Send WETH to an existent account on asset hub
591+
BridgeHubRococo::execute_with(|| {
592+
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
593+
594+
type EthereumInboundQueue =
595+
<BridgeHubRococo as BridgeHubRococoPallet>::EthereumInboundQueue;
596+
let message_id: H256 = [0; 32].into();
597+
let message = VersionedMessage::V1(MessageV1 {
598+
chain_id: CHAIN_ID,
599+
command: Command::SendToken {
600+
token: WETH.into(),
601+
destination: Destination::AccountId32 { id: account_id },
602+
amount: 1_000_000,
603+
fee,
604+
},
605+
});
606+
let (xcm, _) = EthereumInboundQueue::do_convert(message_id, message).unwrap();
607+
assert_ok!(EthereumInboundQueue::send_xcm(xcm, AssetHubRococo::para_id().into()));
608+
609+
// Check that the message was sent
610+
assert_expected_events!(
611+
BridgeHubRococo,
612+
vec![
613+
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
614+
]
615+
);
616+
});
617+
}
618+
619+
#[test]
620+
fn send_token_from_ethereum_to_existent_account_on_asset_hub() {
621+
send_token_from_ethereum_to_asset_hub_with_fee(AssetHubRococoSender::get().into(), XCM_FEE);
622+
623+
AssetHubRococo::execute_with(|| {
624+
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
625+
626+
// Check that the token was received and issued as a foreign asset on AssetHub
627+
assert_expected_events!(
628+
AssetHubRococo,
629+
vec![
630+
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},
631+
]
632+
);
633+
});
634+
}
635+
636+
#[test]
637+
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub() {
638+
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], XCM_FEE);
639+
640+
AssetHubRococo::execute_with(|| {
641+
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
642+
643+
// Check that the token was received and issued as a foreign asset on AssetHub
644+
assert_expected_events!(
645+
AssetHubRococo,
646+
vec![
647+
RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued { .. }) => {},
648+
]
649+
);
650+
});
651+
}
652+
653+
#[test]
654+
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_insufficient_fee() {
655+
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], INSUFFICIENT_XCM_FEE);
656+
657+
AssetHubRococo::execute_with(|| {
658+
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
659+
660+
// Check that the message was not processed successfully due to insufficient fee
661+
662+
assert_expected_events!(
663+
AssetHubRococo,
664+
vec![
665+
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
666+
]
667+
);
668+
});
669+
}
670+
671+
#[test]
672+
fn send_token_from_ethereum_to_non_existent_account_on_asset_hub_with_sufficient_fee_but_do_not_satisfy_ed(
673+
) {
674+
// On AH the xcm fee is 33_873_024 and the ED is 3_300_000
675+
send_token_from_ethereum_to_asset_hub_with_fee([1; 32], 36_000_000);
676+
677+
AssetHubRococo::execute_with(|| {
678+
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
679+
680+
// Check that the message was not processed successfully due to insufficient ED
681+
assert_expected_events!(
682+
AssetHubRococo,
683+
vec![
684+
RuntimeEvent::MessageQueue(pallet_message_queue::Event::Processed { success:false, .. }) => {},
685+
]
686+
);
687+
});
688+
}

prdoc/pr_4175.prdoc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0
2+
# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json
3+
4+
title: "Snowbridge: deposit extra fee to beneficiary on Asset Hub"
5+
6+
doc:
7+
- audience: Runtime Dev
8+
description: |
9+
Snowbridge transfers arriving on Asset Hub will deposit both asset and fees to beneficiary so the fees will not get trapped.
10+
Another benefit is when fees left more than ED, could be used to create the beneficiary account in case it does not exist on asset hub.
11+
12+
crates:
13+
- name: snowbridge-router-primitives

0 commit comments

Comments
 (0)