diff --git a/Cargo.lock b/Cargo.lock index 18efc603d..c362c9bb4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4996,7 +4996,7 @@ dependencies = [ [[package]] name = "hydradx-runtime" -version = "259.0.0" +version = "260.0.0" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", @@ -12259,7 +12259,7 @@ dependencies = [ [[package]] name = "runtime-integration-tests" -version = "1.23.6" +version = "1.23.7" dependencies = [ "cumulus-pallet-aura-ext", "cumulus-pallet-parachain-system", diff --git a/integration-tests/Cargo.toml b/integration-tests/Cargo.toml index 4b8ed5238..a5f2455a2 100644 --- a/integration-tests/Cargo.toml +++ b/integration-tests/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "runtime-integration-tests" -version = "1.23.6" +version = "1.23.7" description = "Integration tests" authors = ["GalacticCouncil"] edition = "2021" diff --git a/integration-tests/src/cross_chain_transfer.rs b/integration-tests/src/cross_chain_transfer.rs index ae791985a..0231016bb 100644 --- a/integration-tests/src/cross_chain_transfer.rs +++ b/integration-tests/src/cross_chain_transfer.rs @@ -1,17 +1,19 @@ #![cfg(test)] use crate::polkadot_test_net::Rococo; use crate::polkadot_test_net::*; +use xcm_emulator::ConvertLocation; +use xcm_executor::traits::TransferType; use frame_support::{assert_noop, assert_ok}; -use polkadot_xcm::{v4::prelude::*, VersionedAssets, VersionedXcm}; +use polkadot_xcm::{v4::prelude::*, VersionedAssetId, VersionedAssets, VersionedXcm}; use cumulus_primitives_core::ParaId; use frame_support::dispatch::GetDispatchInfo; use frame_support::storage::with_transaction; use frame_support::traits::OnInitialize; use frame_support::weights::Weight; -use hydradx_runtime::AssetRegistry; +use hydradx_runtime::{AssetRegistry, LocationToAccountId}; use hydradx_traits::{registry::Mutate, AssetKind, Create}; use orml_traits::currency::MultiCurrency; use polkadot_xcm::opaque::v3::{ @@ -653,38 +655,31 @@ fn transfer_foreign_asset_from_acala_to_hydra_should_not_work() { } #[test] -fn transfer_dot_reserve_from_asset_hub_to_hydra_should_not_work() { +fn transfer_dot_from_asset_hub_to_hydra_should_work() { //Arrange TestNet::reset(); Hydra::execute_with(|| { - let _ = with_transaction(|| { - register_foreign_asset(); - assert_ok!(hydradx_runtime::AssetRegistry::set_location( - DOT, - hydradx_runtime::AssetLocation(MultiLocation::new(1, polkadot_xcm::opaque::v3::Junctions::Here)) - )); + add_currency_price(DOT, FixedU128::from(1)); - add_currency_price(FOREIGN_ASSET, FixedU128::from(1)); - add_currency_price(DOT, FixedU128::from(1)); + assert_ok!(hydradx_runtime::Tokens::deposit( + DOT, + &AccountId::from(ALICE), + 3000 * UNITS + )); - TransactionOutcome::Commit(DispatchResult::Ok(())) - }); + assert_ok!(hydradx_runtime::AssetRegistry::set_location( + DOT, + hydradx_runtime::AssetLocation(MultiLocation::new(1, polkadot_xcm::opaque::v3::Junctions::Here)) + )); }); AssetHub::execute_with(|| { let _ = with_transaction(|| { - register_foreign_asset(); register_dot(); TransactionOutcome::Commit(DispatchResult::Ok(())) }); - assert_ok!(hydradx_runtime::Tokens::deposit( - FOREIGN_ASSET, - &AccountId::from(ALICE), - 3000 * UNITS - )); - assert_ok!(hydradx_runtime::Tokens::deposit( DOT, &AccountId::from(ALICE), @@ -704,7 +699,8 @@ fn transfer_dot_reserve_from_asset_hub_to_hydra_should_not_work() { }])), ); - let xcm = xcm_for_deposit_reserve_asset_to_hydra::(dot, bob_beneficiary); + let xcm = + xcm_transfer_reserve_asset_and_deposit_asset_to_hydra::(dot, bob_beneficiary); //Act let res = hydradx_runtime::PolkadotXcm::execute( @@ -724,7 +720,111 @@ fn transfer_dot_reserve_from_asset_hub_to_hydra_should_not_work() { //Assert Hydra::execute_with(|| { - assert_xcm_message_processing_failed(); + assert_xcm_message_processing_passed(); + + let fee = hydradx_runtime::Tokens::free_balance(DOT, &hydradx_runtime::Treasury::account_id()); + assert!(fee > 0, "treasury should have received fees"); + //Check if the foreign asset from Assethub has been deposited successfully + assert_eq!( + hydradx_runtime::Currencies::free_balance(DOT, &AccountId::from(BOB)), + 100 * UNITS - fee + ); + }); +} + +#[test] +fn transfer_dot_from_hydra_to_asset_hub_should_work() { + let init_hydra_para_dot_balance_on_ah = 4000 * UNITS; + let hydra_at_ah = Location::new( + 1, + cumulus_primitives_core::Junctions::X1(Arc::new([cumulus_primitives_core::Junction::Parachain(HYDRA_PARA_ID)])), + ); + + let hydra_parachain_account_at_ah = LocationToAccountId::convert_location(&hydra_at_ah).unwrap(); + + let transfer_amount = 3 * UNITS; + + AssetHub::execute_with(|| { + let _ = with_transaction(|| { + register_dot(); + add_currency_price(DOT, FixedU128::from(1)); + TransactionOutcome::Commit(DispatchResult::Ok(())) + }); + + assert_ok!(hydradx_runtime::Tokens::deposit( + DOT, + &hydra_parachain_account_at_ah, + init_hydra_para_dot_balance_on_ah + )); + }); + + //Arrange + Hydra::execute_with(|| { + let dot_multiloc = MultiLocation::new(1, polkadot_xcm::opaque::v3::Junctions::Here); + + assert_ok!(hydradx_runtime::AssetRegistry::set_location( + DOT, + hydradx_runtime::AssetLocation(dot_multiloc) + )); + + let dot_loc = Location::new(1, cumulus_primitives_core::Junctions::Here); + + let dot: Asset = Asset { + id: cumulus_primitives_core::AssetId(dot_loc.clone()), + fun: Fungible(transfer_amount), + }; + + let bob_beneficiary = Location::new( + 0, + cumulus_primitives_core::Junctions::X1(Arc::new([cumulus_primitives_core::Junction::AccountId32 { + id: BOB, + network: None, + }])), + ); + + let deposit_xcm = Xcm(vec![DepositAsset { + assets: Wild(WildAsset::AllCounted(1)), + beneficiary: bob_beneficiary.clone(), + }]); + + //Act + assert_ok!(hydradx_runtime::PolkadotXcm::transfer_assets_using_type_and_then( + hydradx_runtime::RuntimeOrigin::signed(ALICE.into()), + Box::new(MultiLocation::new(1, X1(Junction::Parachain(ASSET_HUB_PARA_ID),)).into_versioned()), + Box::new(dot.into()), + Box::new(TransferType::DestinationReserve), + Box::new(VersionedAssetId::V4(cumulus_primitives_core::AssetId(dot_loc))), + Box::new(TransferType::DestinationReserve), + Box::new(VersionedXcm::from(deposit_xcm)), + WeightLimit::Unlimited, + )); + + assert_eq!( + hydradx_runtime::Tokens::free_balance(DOT, &AccountId::from(ALICE)), + ALICE_INITIAL_DOT_BALANCE - transfer_amount + ); + }); + + //Needed to process horizontal xcm messages + Rococo::execute_with(|| {}); + + AssetHub::execute_with(|| { + assert_xcm_message_processing_passed(); + + //We check if the hydra parachain account balance is reduced on AH, meaning AH is responsible for reserve tracking + let hydra_sovereign_account_dot_balance = + hydradx_runtime::Currencies::free_balance(DOT, &hydra_parachain_account_at_ah); + assert_eq!( + hydra_sovereign_account_dot_balance, + init_hydra_para_dot_balance_on_ah - transfer_amount + ); + + let fee = hydradx_runtime::Currencies::free_balance(DOT, &hydradx_runtime::Treasury::account_id()); + + assert_eq!( + hydradx_runtime::Currencies::free_balance(DOT, &AccountId::from(BOB)), + transfer_amount - fee + ); }); } @@ -1016,3 +1116,66 @@ fn rococo_xcm_execute_extrinsic_should_be_allowed() { ),); }); } + +fn xcm_transfer_reserve_asset_and_deposit_asset_to_hydra( + assets: Asset, + beneficiary: Location, +) -> VersionedXcm { + use rococo_runtime::xcm_config::BaseXcmWeight; + use xcm_builder::FixedWeightBounds; + use xcm_executor::traits::WeightBounds; + + type Weigher = FixedWeightBounds>; + + let dest = Location::new( + 1, + cumulus_primitives_core::Junctions::X1(Arc::new([cumulus_primitives_core::Junction::Parachain(HYDRA_PARA_ID)])), + ); + + let max_assets = 1 + 1; + + let context = cumulus_primitives_core::Junctions::X2(Arc::new([ + cumulus_primitives_core::Junction::GlobalConsensus(cumulus_primitives_core::NetworkId::Polkadot), + cumulus_primitives_core::Junction::Parachain(ASSET_HUB_PARA_ID), + ])); + + let fee_asset = assets.clone().reanchored(&dest, &context).expect("should reanchor"); + let fees = fee_asset.clone(); + + let weight_limit = { + let mut remote_message = Xcm(vec![ + ReserveAssetDeposited::(assets.clone().into()), + ClearOrigin, + BuyExecution { + fees: fees.clone(), + weight_limit: Limited(Weight::zero()), + }, + DepositAsset { + assets: Wild(AllCounted(max_assets)), + beneficiary: beneficiary.clone(), + }, + ]); + // use local weight for remote message and hope for the best. + let remote_weight = Weigher::weight(&mut remote_message).expect("weighing should not fail"); + Limited(remote_weight) + }; + // executed on remote (on hydra) + let xcm = Xcm(vec![ + //ReserveAssetDeposited(assets.clone()), + BuyExecution { fees, weight_limit }, + DepositAsset { + assets: Wild(AllCounted(max_assets)), + beneficiary, + }, + ]); + // executed on local (AssetHub) + let message = Xcm(vec![ + SetFeesMode { jit_withdraw: true }, + TransferReserveAsset { + assets: assets.into(), + dest, + xcm, + }, + ]); + VersionedXcm::from(message) +} diff --git a/runtime/hydradx/Cargo.toml b/runtime/hydradx/Cargo.toml index 032f8967d..9e9272537 100644 --- a/runtime/hydradx/Cargo.toml +++ b/runtime/hydradx/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "hydradx-runtime" -version = "259.0.0" +version = "260.0.0" authors = ["GalacticCouncil"] edition = "2021" license = "Apache 2.0" diff --git a/runtime/hydradx/src/lib.rs b/runtime/hydradx/src/lib.rs index c7d69f962..e459ccaa8 100644 --- a/runtime/hydradx/src/lib.rs +++ b/runtime/hydradx/src/lib.rs @@ -108,7 +108,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { spec_name: create_runtime_str!("hydradx"), impl_name: create_runtime_str!("hydradx"), authoring_version: 1, - spec_version: 259, + spec_version: 260, impl_version: 0, apis: apis::RUNTIME_API_VERSIONS, transaction_version: 1, diff --git a/runtime/hydradx/src/xcm.rs b/runtime/hydradx/src/xcm.rs index 6fefa9876..bd1af1b6d 100644 --- a/runtime/hydradx/src/xcm.rs +++ b/runtime/hydradx/src/xcm.rs @@ -141,7 +141,29 @@ where } } +pub struct IsDotFrom(PhantomData); +impl ContainsPair for IsDotFrom +where + Origin: Get, +{ + fn contains(asset: &Asset, origin: &Location) -> bool { + let loc = Origin::get(); + &loc == origin + && matches!( + asset, + Asset { + id: AssetId(Location { + parents: 1, + interior: Here + }), + fun: Fungible(_), + }, + ) + } +} + pub type Reserves = ( + IsDotFrom, IsForeignNativeAssetFrom, MultiNativeAsset, ); @@ -220,7 +242,7 @@ parameter_type_with_key! { pub ParachainMinFee: |location: Location| -> Option { #[allow(clippy::match_ref_pats)] // false positive match (location.parents, location.first_interior()) { - (1, Some(Parachain(ASSET_HUB_PARA_ID))) => Some(50_000_000), + (1, Some(Parachain(ASSET_HUB_PARA_ID))) => Some(150_000_000), _ => None, } };