Skip to content

Commit

Permalink
add round trip test
Browse files Browse the repository at this point in the history
  • Loading branch information
alistair-singh committed Dec 21, 2024
1 parent f174719 commit e1a6f18
Show file tree
Hide file tree
Showing 3 changed files with 175 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
pub mod genesis;

pub use bridge_hub_rococo_runtime::{
xcm_config::XcmConfig as BridgeHubRococoXcmConfig, EthereumBeaconClient, EthereumInboundQueue,
self as bridge_hub_rococo_runtime, xcm_config::XcmConfig as BridgeHubRococoXcmConfig,
EthereumBeaconClient, EthereumInboundQueue,
ExistentialDeposit as BridgeHubRococoExistentialDeposit,
RuntimeOrigin as BridgeHubRococoRuntimeOrigin,
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ mod imports {
AssetHubWestendParaPallet as AssetHubWestendPallet,
},
bridge_hub_rococo_emulated_chain::{
bridge_hub_rococo_runtime::bridge_to_ethereum_config::EthereumGatewayAddress,
genesis::ED as BRIDGE_HUB_ROCOCO_ED, BridgeHubRococoExistentialDeposit,
BridgeHubRococoParaPallet as BridgeHubRococoPallet, BridgeHubRococoRuntimeOrigin,
BridgeHubRococoXcmConfig, EthereumBeaconClient, EthereumInboundQueue,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,8 @@ use hex_literal::hex;
use rococo_westend_system_emulated_network::BridgeHubRococoParaSender as BridgeHubRococoSender;
use snowbridge_core::{inbound::InboundQueueFixture, outbound::OperatingMode};
use snowbridge_pallet_inbound_queue_fixtures::{
register_token::make_register_token_message, send_token::make_send_token_message,
send_token_to_penpal::make_send_token_to_penpal_message,
register_token::make_register_token_message, send_native_eth::make_send_native_eth_message,
send_token::make_send_token_message, send_token_to_penpal::make_send_token_to_penpal_message,
};
use snowbridge_pallet_system;
use snowbridge_router_primitives::inbound::{
Expand Down Expand Up @@ -515,6 +515,176 @@ fn send_weth_asset_from_asset_hub_to_ethereum() {
});
}

/// Tests the full cycle of eth transfers:
/// - sending a token to AssetHub
/// - returning the token to Ethereum
#[test]
fn send_eth_asset_from_asset_hub_to_ethereum_and_back() {
let ethereum_network: NetworkId = EthereumNetwork::get().into();
let origin_location = (Parent, Parent, ethereum_network).into();

use ahr_xcm_config::bridging::to_ethereum::DefaultBridgeHubEthereumBaseFee;
let assethub_location = BridgeHubRococo::sibling_location_of(AssetHubRococo::para_id());
let assethub_sovereign = BridgeHubRococo::sovereign_account_id_of(assethub_location);
let ethereum_sovereign: AccountId =
EthereumLocationsConverterFor::<AccountId>::convert_location(&origin_location).unwrap();

AssetHubRococo::force_default_xcm_version(Some(XCM_VERSION));
BridgeHubRococo::force_default_xcm_version(Some(XCM_VERSION));
AssetHubRococo::force_xcm_version(origin_location.clone(), XCM_VERSION);

BridgeHubRococo::fund_accounts(vec![(assethub_sovereign.clone(), INITIAL_FUND)]);
AssetHubRococo::fund_accounts(vec![
(AssetHubRococoReceiver::get(), INITIAL_FUND),
(ethereum_sovereign.clone(), INITIAL_FUND),
]);

// Register ETH
AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;
assert_ok!(<AssetHubRococo as AssetHubRococoPallet>::ForeignAssets::force_create(
RuntimeOrigin::root(),
origin_location.clone(),
ethereum_sovereign.into(),
true,
1000,
));

assert_expected_events!(
AssetHubRococo,
vec![
RuntimeEvent::ForeignAssets(pallet_assets::Event::ForceCreated { .. }) => {},
]
);
});
const ETH_AMOUNT: u128 = 1_000_000_000_000_000_000;

BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
type RuntimeOrigin = <BridgeHubRococo as Chain>::RuntimeOrigin;

// Set the gateway. This is needed because new fixtures use a different gateway address.
assert_ok!(<BridgeHubRococo as Chain>::System::set_storage(
RuntimeOrigin::root(),
vec![(
EthereumGatewayAddress::key().to_vec(),
sp_core::H160(hex!("87d1f7fdfEe7f651FaBc8bFCB6E086C278b77A7d")).encode(),
)],
));

// Construct SendToken message and sent to inbound queue
assert_ok!(send_inbound_message(make_send_native_eth_message()));

// Check that the send token message was sent using xcm
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::XcmpQueue(cumulus_pallet_xcmp_queue::Event::XcmpMessageSent { .. }) => {},
]
);
});

AssetHubRococo::execute_with(|| {
type RuntimeEvent = <AssetHubRococo as Chain>::RuntimeEvent;
type RuntimeOrigin = <AssetHubRococo as Chain>::RuntimeOrigin;

let _issued_event = RuntimeEvent::ForeignAssets(pallet_assets::Event::Issued {
asset_id: origin_location.clone(),
owner: AssetHubRococoReceiver::get().into(),
amount: ETH_AMOUNT,
});
// Check that AssetHub has issued the foreign asset
assert_expected_events!(
AssetHubRococo,
vec![
_issued_event => {},
]
);
let assets =
vec![Asset { id: AssetId(origin_location.clone()), fun: Fungible(ETH_AMOUNT) }];
let multi_assets = VersionedAssets::from(Assets::from(assets));

let destination = origin_location.clone().into();

let beneficiary = VersionedLocation::from(Location::new(
0,
[AccountKey20 { network: None, key: ETHEREUM_DESTINATION_ADDRESS.into() }],
));

let free_balance_before = <AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(
AssetHubRococoReceiver::get(),
);
// Send the Weth back to Ethereum
<AssetHubRococo as AssetHubRococoPallet>::PolkadotXcm::limited_reserve_transfer_assets(
RuntimeOrigin::signed(AssetHubRococoReceiver::get()),
Box::new(destination),
Box::new(beneficiary),
Box::new(multi_assets),
0,
Unlimited,
)
.unwrap();

let _burned_event = RuntimeEvent::ForeignAssets(pallet_assets::Event::Burned {
asset_id: origin_location.clone(),
owner: AssetHubRococoReceiver::get().into(),
balance: ETH_AMOUNT,
});
// Check that AssetHub has issued the foreign asset
let _destination = origin_location.clone();
assert_expected_events!(
AssetHubRococo,
vec![
_burned_event => {},
RuntimeEvent::PolkadotXcm(pallet_xcm::Event::Sent {
destination: _destination, ..
}) => {},
]
);

let free_balance_after = <AssetHubRococo as AssetHubRococoPallet>::Balances::free_balance(
AssetHubRococoReceiver::get(),
);
// Assert at least DefaultBridgeHubEthereumBaseFee charged from the sender
let free_balance_diff = free_balance_before - free_balance_after;
assert!(free_balance_diff > DefaultBridgeHubEthereumBaseFee::get());
});

BridgeHubRococo::execute_with(|| {
type RuntimeEvent = <BridgeHubRococo as Chain>::RuntimeEvent;
// Check that the transfer token back to Ethereum message was queue in the Ethereum
// Outbound Queue
assert_expected_events!(
BridgeHubRococo,
vec![
RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageAccepted {..}) => {},
RuntimeEvent::EthereumOutboundQueue(snowbridge_pallet_outbound_queue::Event::MessageQueued {..}) => {},
]
);

let events = BridgeHubRococo::events();
// Check that the local fee was credited to the Snowbridge sovereign account
assert!(
events.iter().any(|event| matches!(
event,
RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount })
if *who == TREASURY_ACCOUNT.into() && *amount == 16903333
)),
"Snowbridge sovereign takes local fee."
);
// Check that the remote fee was credited to the AssetHub sovereign account
assert!(
events.iter().any(|event| matches!(
event,
RuntimeEvent::Balances(pallet_balances::Event::Minted { who, amount })
if *who == assethub_sovereign && *amount == 2680000000000,
)),
"AssetHub sovereign takes remote fee."
);
});
}

#[test]
fn send_token_from_ethereum_to_asset_hub_fail_for_insufficient_fund() {
// Insufficient fund
Expand Down

0 comments on commit e1a6f18

Please sign in to comment.