From d46225cf17bf85604bee48de8fbf4bd04588dffd Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Wed, 2 Oct 2024 17:17:40 -0700 Subject: [PATCH 01/12] start adding transfer_assets_using_type_and_then to pallet-xcm precompile --- precompiles/pallet-xcm/Cargo.toml | 5 + precompiles/pallet-xcm/XcmInterface.sol | 32 +++++++ precompiles/pallet-xcm/src/lib.rs | 120 +++++++++++++++++++++++- 3 files changed, 155 insertions(+), 2 deletions(-) diff --git a/precompiles/pallet-xcm/Cargo.toml b/precompiles/pallet-xcm/Cargo.toml index 526a52ea..fb70559d 100644 --- a/precompiles/pallet-xcm/Cargo.toml +++ b/precompiles/pallet-xcm/Cargo.toml @@ -16,10 +16,12 @@ xcm-primitives = { workspace = true } # Substrate frame-support = { workspace = true } frame-system = { workspace = true } +scale-info = { workspace = true } sp-core = { workspace = true } sp-runtime = { workspace = true } sp-std = { workspace = true } sp-weights = { workspace = true } +parity-scale-codec = { workspace = true, features = [ "derive" ] } # Frontier evm = { workspace = true, features = [ "with-codec" ] } @@ -28,6 +30,7 @@ pallet-evm = { workspace = true, features = [ "forbid-evm-reentrancy" ] } # Polkadot xcm = { workspace = true } +xcm-executor = { workspace = true } pallet-xcm = { workspace = true } # Cumulus @@ -61,7 +64,9 @@ std = [ "frame-system/std", "pallet-evm/std", "pallet-xcm/std", + "parity-scale-codec/std", "precompile-utils/std", + "scale-info/std", "sp-core/std", "sp-std/std", "xcm/std", diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index 303bad53..8f01cb43 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -29,6 +29,14 @@ interface XCM { uint256 amount; } + // The values start at `0` and are represented as `uint8` + enum TransferType { + Teleport, + LocalReserve, + DestinationReserve, + RemoteReserve + } + /// @dev Function to send assets via XCM using transfer_assets() pallet-xcm extrinsic. /// @custom:selector 59df8416 /// @param dest The destination chain. @@ -93,4 +101,28 @@ interface XCM { uint32 feeAssetItem, Weight memory weight ) external; + + function transferAssetsUsingTypeAndThenLocation( + Location memory dest, + AssetLocationInfo[] memory assets, + TransferType assetsTransferType, + Location memory maybeAssetsRemoteReserve, + uint8 remoteFeesIdIndex, + TransferType feesTransferType, + Location memory maybeFeesRemoteReserve, + bytes memory customXcmOnDest, + Weight memory weight + ) external; + + function transferAssetsUsingTypeAndThenAddress( + Location memory dest, + AssetAddressInfo[] memory assets, + TransferType assetsTransferType, + Location memory maybeAssetsRemoteReserve, + uint8 remoteFeesIdIndex, + TransferType feesTransferType, + Location memory maybeFeesRemoteReserve, + bytes memory customXcmOnDest, + Weight memory weight + ) external; } \ No newline at end of file diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index 0834e0f1..13f027e9 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -22,17 +22,20 @@ use frame_support::{ traits::ConstU32, }; use pallet_evm::AddressMapping; +use parity_scale_codec::{Decode, DecodeLimit, Encode, MaxEncodedLen}; use precompile_utils::prelude::*; -use sp_core::{MaxEncodedLen, H256, U256}; +use scale_info::TypeInfo; +use sp_core::{H256, U256}; use sp_runtime::traits::Dispatchable; use sp_std::{boxed::Box, marker::PhantomData, vec, vec::Vec}; use sp_weights::Weight; use xcm::{ latest::{Asset, AssetId, Assets, Fungibility, Location}, prelude::WeightLimit::*, - VersionedAssets, VersionedLocation, + VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, MAX_XCM_DECODE_DEPTH, }; +use xcm_executor::traits::TransferType; use xcm_primitives::{ generators::{ XcmLocalBeneficiary20Generator, XcmLocalBeneficiary32Generator, @@ -49,6 +52,19 @@ mod tests; pub const MAX_ASSETS_ARRAY_LIMIT: u32 = 2; type GetArrayLimit = ConstU32; +pub const XCM_SIZE_LIMIT: u32 = 2u32.pow(16); +type GetXcmSizeLimit = ConstU32; + +#[derive( + Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Encode, Decode, Debug, MaxEncodedLen, TypeInfo, +)] +pub enum TransferTypeHelper { + Teleport = 0, + LocalReserve = 1, + DestinationReserve = 2, + RemoteReserve = 3, +} + pub struct PalletXcmPrecompile(PhantomData<(Runtime, LocationMatcher)>); #[precompile_utils::precompile] @@ -259,6 +275,85 @@ where Ok(()) } + #[precompile::public( + "transferAssetsUsingTypeAndThenLocation(\ + (uint8,bytes[]),\ + ((uint8,bytes[]),uint256)[],\ + uint8,\ + (uint8,bytes[]),\ + uint8,\ + uint8,\ + (uint8,bytes[]),\ + bytes,\ + (uint64,uint64))" + )] + fn transfer_assets_using_type_and_then_location( + handle: &mut impl PrecompileHandle, + dest: Location, + assets: BoundedVec<(Location, Convert), GetArrayLimit>, + assets_transfer_type: u8, + maybe_assets_remote_reserve: Location, + remote_fees_id_index: u8, + fees_transfer_type: u8, + maybe_fees_remote_reserve: Location, + custom_xcm_on_dest: BoundedBytes, + weight: Weight, + ) -> EvmResult { + // No DB access before try_dispatch but some logical stuff. + // To prevent spam, we charge an arbitrary amount of gas. + handle.record_cost(1000)?; + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let assets: Vec<_> = assets.into(); + let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); + + let remote_fees_id: AssetId = { + let asset = assets + .get(remote_fees_id_index as usize) + .ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + AssetId(asset.0.clone()) + }; + + let assets_to_send: Assets = assets + .into_iter() + .map(|asset| Asset { + id: AssetId(asset.0), + fun: Fungibility::Fungible(asset.1.converted()), + }) + .collect::>() + .into(); + + let weight_limit = match weight.ref_time() { + u64::MAX => Unlimited, + _ => Limited(weight), + }; + + let assets_transfer_type = + Self::check_transfer_type(assets_transfer_type, maybe_assets_remote_reserve)?; + let fees_transfer_type = + Self::check_transfer_type(fees_transfer_type, maybe_fees_remote_reserve)?; + + let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut custom_xcm_on_dest.as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { + dest: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets_to_send)), + assets_transfer_type: Box::new(assets_transfer_type), + remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), + fees_transfer_type: Box::new(fees_transfer_type), + custom_xcm_on_dest: Box::new(custom_xcm_on_dest), + weight_limit, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + // Helper function to convert and prepare each asset into a proper Location. fn check_and_prepare_assets( assets: Vec<(Address, Convert)>, @@ -277,4 +372,25 @@ where } Ok(assets_to_send) } + + fn check_transfer_type( + transfer_type: u8, + remote_reserve: Location, + ) -> Result { + let transfer_type_helper: TransferTypeHelper = TransferTypeHelper::decode( + &mut transfer_type.to_le_bytes().as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding value for TransferTypeHelper"))?; + + match transfer_type_helper { + TransferTypeHelper::Teleport => return Ok(TransferType::Teleport), + TransferTypeHelper::LocalReserve => return Ok(TransferType::LocalReserve), + TransferTypeHelper::DestinationReserve => return Ok(TransferType::DestinationReserve), + TransferTypeHelper::RemoteReserve => { + return Ok(TransferType::RemoteReserve(VersionedLocation::V4( + remote_reserve, + ))) + } + } + } } From 76702b23127f911b7306129fdcb4dde65e15559b Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Wed, 2 Oct 2024 17:44:49 -0700 Subject: [PATCH 02/12] add function variant for address type --- precompiles/pallet-xcm/src/lib.rs | 87 +++++++++++++++++++++++++++++-- 1 file changed, 82 insertions(+), 5 deletions(-) diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index 13f027e9..0d86c531 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -354,6 +354,81 @@ where Ok(()) } + #[precompile::public( + "transferAssetsUsingTypeAndThenAddress(\ + (uint8,bytes[]),\ + (address,uint256)[],\ + uint8,\ + (uint8,bytes[]),\ + uint8,\ + uint8,\ + (uint8,bytes[]),\ + bytes,\ + (uint64,uint64))" + )] + fn transfer_assets_using_type_and_then_address( + handle: &mut impl PrecompileHandle, + dest: Location, + assets: BoundedVec<(Address, Convert), GetArrayLimit>, + assets_transfer_type: u8, + maybe_assets_remote_reserve: Location, + remote_fees_id_index: u8, + fees_transfer_type: u8, + maybe_fees_remote_reserve: Location, + custom_xcm_on_dest: BoundedBytes, + weight: Weight, + ) -> EvmResult { + // Account for a possible storage read inside LocationMatcher::convert(). + // + // Storage items: AssetIdToForeignAsset (ForeignAssetCreator pallet) or AssetIdType (AssetManager pallet). + // + // Blake2_128(16) + AssetId(16) + Location + handle.record_db_read::(32 + Location::max_encoded_len())?; + handle.record_cost(1000)?; + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let assets: Vec<_> = assets.into(); + let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); + + let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; + let remote_fees_id: AssetId = { + let asset = assets_to_send + .get(remote_fees_id_index as usize) + .ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + AssetId(asset.id.0.clone()) + }; + + let weight_limit = match weight.ref_time() { + u64::MAX => Unlimited, + _ => Limited(weight), + }; + + let assets_transfer_type = + Self::check_transfer_type(assets_transfer_type, maybe_assets_remote_reserve)?; + let fees_transfer_type = + Self::check_transfer_type(fees_transfer_type, maybe_fees_remote_reserve)?; + + let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut custom_xcm_on_dest.as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { + dest: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets_to_send.into())), + assets_transfer_type: Box::new(assets_transfer_type), + remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), + fees_transfer_type: Box::new(fees_transfer_type), + custom_xcm_on_dest: Box::new(custom_xcm_on_dest), + weight_limit, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + // Helper function to convert and prepare each asset into a proper Location. fn check_and_prepare_assets( assets: Vec<(Address, Convert)>, @@ -362,13 +437,15 @@ where for asset in assets { let asset_account = Runtime::AddressMapping::into_account_id(asset.0 .0); let asset_location = LocationMatcher::convert(asset_account); - if asset_location == None { + + if let Some(asset_loc) = asset_location { + assets_to_send.push(Asset { + id: AssetId(asset_loc), + fun: Fungibility::Fungible(asset.1.converted()), + }) + } else { return Err(revert("Asset not found")); } - assets_to_send.push(Asset { - id: AssetId(asset_location.unwrap_or_default()), - fun: Fungibility::Fungible(asset.1.converted()), - }) } Ok(assets_to_send) } From cada24d43f985b0996e3a814ddf75a0d796a45eb Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Thu, 3 Oct 2024 06:01:41 -0700 Subject: [PATCH 03/12] use WeightLimit::Unlimited in all selectors --- precompiles/pallet-xcm/XcmInterface.sol | 34 ++++--------- precompiles/pallet-xcm/src/lib.rs | 68 +++++-------------------- precompiles/pallet-xcm/src/tests.rs | 23 ++------- 3 files changed, 27 insertions(+), 98 deletions(-) diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index 8f01cb43..e410236f 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -38,68 +38,56 @@ interface XCM { } /// @dev Function to send assets via XCM using transfer_assets() pallet-xcm extrinsic. - /// @custom:selector 59df8416 + /// @custom:selector 9ea8ada7 /// @param dest The destination chain. /// @param beneficiary The actual account that will receive the tokens on dest. /// @param assets The combination (array) of assets to send. /// @param feeAssetItem The index of the asset that will be used to pay for fees. - /// @param weight The weight to be used for the whole XCM operation. - /// (uint64::MAX in refTime means Unlimited weight) function transferAssetsLocation( Location memory dest, Location memory beneficiary, AssetLocationInfo[] memory assets, - uint32 feeAssetItem, - Weight memory weight + uint32 feeAssetItem ) external; /// @dev Function to send assets via XCM to a 20 byte-like parachain /// using transfer_assets() pallet-xcm extrinsic. - /// @custom:selector b489262e + /// @custom:selector a0aeb5fe /// @param paraId The para-id of the destination chain. /// @param beneficiary The actual account that will receive the tokens on paraId destination. /// @param assets The combination (array) of assets to send. /// @param feeAssetItem The index of the asset that will be used to pay for fees. - /// @param weight The weight to be used for the whole XCM operation. - /// (uint64::MAX in refTime means Unlimited weight) function transferAssetsToPara20( uint32 paraId, address beneficiary, AssetAddressInfo[] memory assets, - uint32 feeAssetItem, - Weight memory weight + uint32 feeAssetItem ) external; /// @dev Function to send assets via XCM to a 32 byte-like parachain /// using transfer_assets() pallet-xcm extrinsic. - /// @custom:selector 4461e6f5 + /// @custom:selector f23032c3 /// @param paraId The para-id of the destination chain. /// @param beneficiary The actual account that will receive the tokens on paraId destination. /// @param assets The combination (array) of assets to send. /// @param feeAssetItem The index of the asset that will be used to pay for fees. - /// @param weight The weight to be used for the whole XCM operation. - /// (uint64::MAX in refTime means Unlimited weight) function transferAssetsToPara32( uint32 paraId, bytes32 beneficiary, AssetAddressInfo[] memory assets, - uint32 feeAssetItem, - Weight memory weight + uint32 feeAssetItem ) external; /// @dev Function to send assets via XCM to the relay chain /// using transfer_assets() pallet-xcm extrinsic. - /// @custom:selector d7c89659 + /// @custom:selector 6521cc2c /// @param beneficiary The actual account that will receive the tokens on the relay chain. /// @param assets The combination (array) of assets to send. /// @param feeAssetItem The index of the asset that will be used to pay for fees. - /// @param weight The weight to be used for the whole XCM operation. - /// (uint64::MAX in refTime means Unlimited weight) function transferAssetsToRelay( bytes32 beneficiary, AssetAddressInfo[] memory assets, - uint32 feeAssetItem, - Weight memory weight + uint32 feeAssetItem ) external; function transferAssetsUsingTypeAndThenLocation( @@ -110,8 +98,7 @@ interface XCM { uint8 remoteFeesIdIndex, TransferType feesTransferType, Location memory maybeFeesRemoteReserve, - bytes memory customXcmOnDest, - Weight memory weight + bytes memory customXcmOnDest ) external; function transferAssetsUsingTypeAndThenAddress( @@ -122,7 +109,6 @@ interface XCM { uint8 remoteFeesIdIndex, TransferType feesTransferType, Location memory maybeFeesRemoteReserve, - bytes memory customXcmOnDest, - Weight memory weight + bytes memory customXcmOnDest ) external; } \ No newline at end of file diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index 0d86c531..37987644 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -31,7 +31,7 @@ use sp_runtime::traits::Dispatchable; use sp_std::{boxed::Box, marker::PhantomData, vec, vec::Vec}; use sp_weights::Weight; use xcm::{ - latest::{Asset, AssetId, Assets, Fungibility, Location}, + latest::{Asset, AssetId, Assets, Fungibility, Location, WeightLimit}, prelude::WeightLimit::*, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, MAX_XCM_DECODE_DEPTH, }; @@ -83,8 +83,7 @@ where (uint8,bytes[]),\ (uint8,bytes[]),\ ((uint8,bytes[]),uint256)[],\ - uint32,\ - (uint64,uint64))" + uint32)" )] fn transfer_assets_location( handle: &mut impl PrecompileHandle, @@ -92,7 +91,6 @@ where beneficiary: Location, assets: BoundedVec<(Location, Convert), GetArrayLimit>, fee_asset_item: u32, - weight: Weight, ) -> EvmResult { // No DB access before try_dispatch but some logical stuff. // To prevent spam, we charge an arbitrary amount of gas. @@ -110,17 +108,12 @@ where .collect::>() .into(); - let weight_limit = match weight.ref_time() { - u64::MAX => Unlimited, - _ => Limited(weight), - }; - let call = pallet_xcm::Call::::transfer_assets { dest: Box::new(VersionedLocation::V4(dest)), beneficiary: Box::new(VersionedLocation::V4(beneficiary)), assets: Box::new(VersionedAssets::V4(assets_to_send)), fee_asset_item, - weight_limit, + weight_limit: WeightLimit::Unlimited, }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; @@ -132,8 +125,7 @@ where uint32,\ address,\ (address,uint256)[],\ - uint32,\ - (uint64,uint64))" + uint32)" )] fn transfer_assets_to_para_20( handle: &mut impl PrecompileHandle, @@ -141,7 +133,6 @@ where beneficiary: Address, assets: BoundedVec<(Address, Convert), GetArrayLimit>, fee_asset_item: u32, - weight: Weight, ) -> EvmResult { // Account for a possible storage read inside LocationMatcher::convert(). // @@ -156,11 +147,6 @@ where let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; - let weight_limit = match weight.ref_time() { - u64::MAX => Unlimited, - _ => Limited(weight), - }; - let dest = XcmSiblingDestinationGenerator::generate(para_id); let beneficiary = XcmLocalBeneficiary20Generator::generate(beneficiary.0 .0); @@ -169,7 +155,7 @@ where beneficiary: Box::new(VersionedLocation::V4(beneficiary)), assets: Box::new(VersionedAssets::V4(assets_to_send.into())), fee_asset_item, - weight_limit, + weight_limit: WeightLimit::Unlimited, }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; @@ -182,8 +168,7 @@ where uint32,\ bytes32,\ (address,uint256)[],\ - uint32,\ - (uint64,uint64))" + uint32)" )] fn transfer_assets_to_para_32( handle: &mut impl PrecompileHandle, @@ -191,7 +176,6 @@ where beneficiary: H256, assets: BoundedVec<(Address, Convert), GetArrayLimit>, fee_asset_item: u32, - weight: Weight, ) -> EvmResult { // Account for a possible storage read inside LocationMatcher::convert(). // @@ -206,11 +190,6 @@ where let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; - let weight_limit = match weight.ref_time() { - u64::MAX => Unlimited, - _ => Limited(weight), - }; - let dest = XcmSiblingDestinationGenerator::generate(para_id); let beneficiary = XcmLocalBeneficiary32Generator::generate(beneficiary.0); @@ -219,7 +198,7 @@ where beneficiary: Box::new(VersionedLocation::V4(beneficiary)), assets: Box::new(VersionedAssets::V4(assets_to_send.into())), fee_asset_item, - weight_limit, + weight_limit: WeightLimit::Unlimited, }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; @@ -231,15 +210,13 @@ where "transferAssetsToRelay(\ bytes32,\ (address,uint256)[],\ - uint32,\ - (uint64,uint64))" + uint32)" )] fn transfer_assets_to_relay( handle: &mut impl PrecompileHandle, beneficiary: H256, assets: BoundedVec<(Address, Convert), GetArrayLimit>, fee_asset_item: u32, - weight: Weight, ) -> EvmResult { // Account for a possible storage read inside LocationMatcher::convert(). // @@ -254,11 +231,6 @@ where let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; - let weight_limit = match weight.ref_time() { - u64::MAX => Unlimited, - _ => Limited(weight), - }; - let dest = Location::parent(); let beneficiary = XcmLocalBeneficiary32Generator::generate(beneficiary.0); @@ -267,7 +239,7 @@ where beneficiary: Box::new(VersionedLocation::V4(beneficiary)), assets: Box::new(VersionedAssets::V4(assets_to_send.into())), fee_asset_item, - weight_limit, + weight_limit: WeightLimit::Unlimited, }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; @@ -284,8 +256,7 @@ where uint8,\ uint8,\ (uint8,bytes[]),\ - bytes,\ - (uint64,uint64))" + bytes)" )] fn transfer_assets_using_type_and_then_location( handle: &mut impl PrecompileHandle, @@ -297,7 +268,6 @@ where fees_transfer_type: u8, maybe_fees_remote_reserve: Location, custom_xcm_on_dest: BoundedBytes, - weight: Weight, ) -> EvmResult { // No DB access before try_dispatch but some logical stuff. // To prevent spam, we charge an arbitrary amount of gas. @@ -323,11 +293,6 @@ where .collect::>() .into(); - let weight_limit = match weight.ref_time() { - u64::MAX => Unlimited, - _ => Limited(weight), - }; - let assets_transfer_type = Self::check_transfer_type(assets_transfer_type, maybe_assets_remote_reserve)?; let fees_transfer_type = @@ -346,7 +311,7 @@ where remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), fees_transfer_type: Box::new(fees_transfer_type), custom_xcm_on_dest: Box::new(custom_xcm_on_dest), - weight_limit, + weight_limit: WeightLimit::Unlimited, }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; @@ -363,8 +328,7 @@ where uint8,\ uint8,\ (uint8,bytes[]),\ - bytes,\ - (uint64,uint64))" + bytes)" )] fn transfer_assets_using_type_and_then_address( handle: &mut impl PrecompileHandle, @@ -376,7 +340,6 @@ where fees_transfer_type: u8, maybe_fees_remote_reserve: Location, custom_xcm_on_dest: BoundedBytes, - weight: Weight, ) -> EvmResult { // Account for a possible storage read inside LocationMatcher::convert(). // @@ -398,11 +361,6 @@ where AssetId(asset.id.0.clone()) }; - let weight_limit = match weight.ref_time() { - u64::MAX => Unlimited, - _ => Limited(weight), - }; - let assets_transfer_type = Self::check_transfer_type(assets_transfer_type, maybe_assets_remote_reserve)?; let fees_transfer_type = @@ -421,7 +379,7 @@ where remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), fees_transfer_type: Box::new(fees_transfer_type), custom_xcm_on_dest: Box::new(custom_xcm_on_dest), - weight_limit, + weight_limit: WeightLimit::Unlimited, }; RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index fec474b9..27915b52 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -33,10 +33,10 @@ fn test_solidity_interface_has_all_function_selectors_documented_and_implemented #[test] fn selectors() { - assert!(PCall::transfer_assets_location_selectors().contains(&0x59df8416)); - assert!(PCall::transfer_assets_to_para_20_selectors().contains(&0xb489262e)); - assert!(PCall::transfer_assets_to_para_32_selectors().contains(&0x4461e6f5)); - assert!(PCall::transfer_assets_to_relay_selectors().contains(&0xd7c89659)); + assert!(PCall::transfer_assets_location_selectors().contains(&0x9ea8ada7)); + assert!(PCall::transfer_assets_to_para_20_selectors().contains(&0xa0aeb5fe)); + assert!(PCall::transfer_assets_to_para_32_selectors().contains(&0xf23032c3)); + assert!(PCall::transfer_assets_to_relay_selectors().contains(&0x6521cc2c)); } #[test] @@ -101,9 +101,6 @@ fn test_transfer_assets_works() { ] .into(), fee_asset_item: 0u32, - // As we are indicating u64::MAX in ref_time, an Unlimited variant - // will be applied at the end. - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001001) @@ -147,9 +144,6 @@ fn test_transfer_assets_success_when_paying_fees_with_foreign_asset() { // We also act as a reserve for the foreign asset thus when can pay local // fees with it. fee_asset_item: 1u32, - // As we are indicating u64::MAX in ref_time, an Unlimited variant - // will be applied at the end. - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001001) @@ -192,9 +186,6 @@ fn test_transfer_assets_fails_fees_unknown_reserve() { .into(), // No reserve will be found for this asset. fee_asset_item: 1u32, - // As we are indicating u64::MAX in ref_time, an Unlimited variant - // will be applied at the end. - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_no_logs() @@ -220,7 +211,6 @@ fn test_transfer_assets_to_para_20_native_asset() { beneficiary: Address(Bob.into()), assets: vec![(Address(pallet_balances_address), 500.into())].into(), fee_asset_item: 0u32, - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001002) @@ -247,7 +237,6 @@ fn test_transfer_assets_to_para_32_native_asset() { beneficiary: H256([1u8; 32]), assets: vec![(Address(pallet_balances_address), 500.into())].into(), fee_asset_item: 0u32, - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001002) @@ -273,7 +262,6 @@ fn test_transfer_assets_to_relay_native_asset() { beneficiary: H256([1u8; 32]), assets: vec![(Address(pallet_balances_address), 500.into())].into(), fee_asset_item: 0u32, - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001002) @@ -316,7 +304,6 @@ fn test_transfer_assets_to_para_20_foreign_asset() { ] .into(), fee_asset_item: 0u32, - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001002) @@ -359,7 +346,6 @@ fn test_transfer_assets_to_para_32_foreign_asset() { ] .into(), fee_asset_item: 0u32, - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001002) @@ -401,7 +387,6 @@ fn test_transfer_assets_to_relay_foreign_asset() { ] .into(), fee_asset_item: 0u32, - weight: Weight::from_parts(u64::MAX, 80000), }, ) .expect_cost(100001002) From b1ef0fa8dd5650e0b75d86ec5bcb2465188f39da Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Thu, 3 Oct 2024 15:32:15 -0700 Subject: [PATCH 04/12] add remaining functions and overall refactor --- precompiles/pallet-xcm/XcmInterface.sol | 50 ++- precompiles/pallet-xcm/src/lib.rs | 389 ++++++++++++++++++++---- precompiles/pallet-xcm/src/tests.rs | 1 - 3 files changed, 380 insertions(+), 60 deletions(-) diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index e410236f..aa2df595 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -90,25 +90,67 @@ interface XCM { uint32 feeAssetItem ) external; + // No reserves at all function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, TransferType assetsTransferType, - Location memory maybeAssetsRemoteReserve, uint8 remoteFeesIdIndex, TransferType feesTransferType, - Location memory maybeFeesRemoteReserve, bytes memory customXcmOnDest ) external; + // Reserve for assets or fees (specified through isAssetReserve) + function transferAssetsUsingTypeAndThenLocation( + Location memory dest, + AssetLocationInfo[] memory assets, + TransferType assetsTransferType, + uint8 remoteFeesIdIndex, + TransferType feesTransferType, + bytes memory customXcmOnDest, + Location memory assetsOrFeeRemoteReserve, + bool isAssetsReserve + ) external; + + // Reserve for both assets and fees + function transferAssetsUsingTypeAndThenLocation( + Location memory dest, + AssetLocationInfo[] memory assets, + uint8 remoteFeesIdIndex, + bytes memory customXcmOnDest, + Location memory assetsRemoteReserve, + Location memory feesRemoteReserve + ) external; + + // No reserves at all function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, TransferType assetsTransferType, - Location memory maybeAssetsRemoteReserve, uint8 remoteFeesIdIndex, TransferType feesTransferType, - Location memory maybeFeesRemoteReserve, bytes memory customXcmOnDest ) external; + + // Reserve for assets or fees (specified through isAssetReserve) + function transferAssetsUsingTypeAndThenAddress( + Location memory dest, + AssetAddressInfo[] memory assets, + TransferType assetsTransferType, + uint8 remoteFeesIdIndex, + TransferType feesTransferType, + bytes memory customXcmOnDest, + Location memory assetsOrFeeRemoteReserve, + bool isAssetsReserve + ) external; + + // Reserve for both assets and fees + function transferAssetsUsingTypeAndThenAddress( + Location memory dest, + AssetAddressInfo[] memory assets, + uint8 remoteFeesIdIndex, + bytes memory customXcmOnDest, + Location memory assetsRemoteReserve, + Location memory feesRemoteReserve + ) external; } \ No newline at end of file diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index 37987644..a9983e96 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -29,10 +29,8 @@ use scale_info::TypeInfo; use sp_core::{H256, U256}; use sp_runtime::traits::Dispatchable; use sp_std::{boxed::Box, marker::PhantomData, vec, vec::Vec}; -use sp_weights::Weight; use xcm::{ latest::{Asset, AssetId, Assets, Fungibility, Location, WeightLimit}, - prelude::WeightLimit::*, VersionedAssetId, VersionedAssets, VersionedLocation, VersionedXcm, MAX_XCM_DECODE_DEPTH, }; use xcm_executor::traits::TransferType; @@ -99,14 +97,7 @@ where let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let assets: Vec<_> = assets.into(); - let assets_to_send: Assets = assets - .into_iter() - .map(|asset| Asset { - id: AssetId(asset.0), - fun: Fungibility::Fungible(asset.1.converted()), - }) - .collect::>() - .into(); + let (assets_to_send, _) = Self::get_assets_to_send_and_remote_fees(assets, None)?; let call = pallet_xcm::Call::::transfer_assets { dest: Box::new(VersionedLocation::V4(dest)), @@ -145,7 +136,8 @@ where let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let assets: Vec<_> = assets.into(); - let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; + let assets_converted = Self::convert_assets(assets)?; + let (assets_to_send, _) = Self::get_assets_to_send_and_remote_fees(assets_converted, None)?; let dest = XcmSiblingDestinationGenerator::generate(para_id); let beneficiary = XcmLocalBeneficiary20Generator::generate(beneficiary.0 .0); @@ -188,7 +180,8 @@ where let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let assets: Vec<_> = assets.into(); - let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; + let assets_converted = Self::convert_assets(assets)?; + let (assets_to_send, _) = Self::get_assets_to_send_and_remote_fees(assets_converted, None)?; let dest = XcmSiblingDestinationGenerator::generate(para_id); let beneficiary = XcmLocalBeneficiary32Generator::generate(beneficiary.0); @@ -229,7 +222,8 @@ where let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); let assets: Vec<_> = assets.into(); - let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; + let assets_converted = Self::convert_assets(assets)?; + let (assets_to_send, _) = Self::get_assets_to_send_and_remote_fees(assets_converted, None)?; let dest = Location::parent(); let beneficiary = XcmLocalBeneficiary32Generator::generate(beneficiary.0); @@ -247,27 +241,86 @@ where Ok(()) } + // First selector type: no reserves at all #[precompile::public( "transferAssetsUsingTypeAndThenLocation(\ (uint8,bytes[]),\ ((uint8,bytes[]),uint256)[],\ uint8,\ + uint8,\ + uint8,\ + bytes)" + )] + fn transfer_assets_using_type_and_then_location_no_reserves( + handle: &mut impl PrecompileHandle, + dest: Location, + assets: BoundedVec<(Location, Convert), GetArrayLimit>, + assets_transfer_type: u8, + remote_fees_id_index: u8, + fees_transfer_type: u8, + custom_xcm_on_dest: BoundedBytes, + ) -> EvmResult { + // No DB access before try_dispatch but some logical stuff. + // To prevent spam, we charge an arbitrary amount of gas. + handle.record_cost(1000)?; + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let assets: Vec<_> = assets.into(); + let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); + + let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( + assets.clone(), + Some(remote_fees_id_index as usize), + )?; + let remote_fees_id = + remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + + let assets_transfer_type = Self::check_transfer_type(assets_transfer_type, None)?; + let fees_transfer_type = Self::check_transfer_type(fees_transfer_type, None)?; + + let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut custom_xcm_on_dest.as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { + dest: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets_to_send)), + assets_transfer_type: Box::new(assets_transfer_type), + remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), + fees_transfer_type: Box::new(fees_transfer_type), + custom_xcm_on_dest: Box::new(custom_xcm_on_dest), + weight_limit: WeightLimit::Unlimited, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + // Either assets reserve or fees reserve + #[precompile::public( + "transferAssetsUsingTypeAndThenLocation(\ (uint8,bytes[]),\ + ((uint8,bytes[]),uint256)[],\ + uint8,\ uint8,\ uint8,\ + bytes,\ (uint8,bytes[]),\ - bytes)" + bool)" )] - fn transfer_assets_using_type_and_then_location( + fn transfer_assets_using_type_and_then_location_assets_or_fee_reserve( handle: &mut impl PrecompileHandle, dest: Location, assets: BoundedVec<(Location, Convert), GetArrayLimit>, assets_transfer_type: u8, - maybe_assets_remote_reserve: Location, remote_fees_id_index: u8, fees_transfer_type: u8, - maybe_fees_remote_reserve: Location, custom_xcm_on_dest: BoundedBytes, + assets_or_fee_remote_reserve: Location, + is_assets_reserve: bool, ) -> EvmResult { // No DB access before try_dispatch but some logical stuff. // To prevent spam, we charge an arbitrary amount of gas. @@ -277,26 +330,88 @@ where let assets: Vec<_> = assets.into(); let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); - let remote_fees_id: AssetId = { - let asset = assets - .get(remote_fees_id_index as usize) - .ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - AssetId(asset.0.clone()) + let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( + assets.clone(), + Some(remote_fees_id_index as usize), + )?; + let remote_fees_id = + remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + + let (assets_transfer_type, fees_transfer_type) = if is_assets_reserve { + ( + Self::check_transfer_type( + assets_transfer_type, + Some(assets_or_fee_remote_reserve), + )?, + Self::check_transfer_type(fees_transfer_type, None)?, + ) + } else { + ( + Self::check_transfer_type(assets_transfer_type, None)?, + Self::check_transfer_type(fees_transfer_type, Some(assets_or_fee_remote_reserve))?, + ) }; - let assets_to_send: Assets = assets - .into_iter() - .map(|asset| Asset { - id: AssetId(asset.0), - fun: Fungibility::Fungible(asset.1.converted()), - }) - .collect::>() - .into(); + let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut custom_xcm_on_dest.as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { + dest: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets_to_send)), + assets_transfer_type: Box::new(assets_transfer_type), + remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), + fees_transfer_type: Box::new(fees_transfer_type), + custom_xcm_on_dest: Box::new(custom_xcm_on_dest), + weight_limit: WeightLimit::Unlimited, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + // Both reserves for assets and fees + #[precompile::public( + "transferAssetsUsingTypeAndThenLocation(\ + (uint8,bytes[]),\ + ((uint8,bytes[]),uint256)[],\ + uint8,\ + bytes,\ + (uint8,bytes[]),\ + (uint8,bytes[]))" + )] + fn transfer_assets_using_type_and_then_location_both_reserves( + handle: &mut impl PrecompileHandle, + dest: Location, + assets: BoundedVec<(Location, Convert), GetArrayLimit>, + remote_fees_id_index: u8, + custom_xcm_on_dest: BoundedBytes, + assets_remote_reserve: Location, + fees_remote_reserve: Location, + ) -> EvmResult { + // No DB access before try_dispatch but some logical stuff. + // To prevent spam, we charge an arbitrary amount of gas. + handle.record_cost(1000)?; + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let assets: Vec<_> = assets.into(); + let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); + + let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( + assets.clone(), + Some(remote_fees_id_index as usize), + )?; + let remote_fees_id = + remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + + let remote_reserve_transfer_type: u8 = TransferTypeHelper::RemoteReserve as u8; let assets_transfer_type = - Self::check_transfer_type(assets_transfer_type, maybe_assets_remote_reserve)?; + Self::check_transfer_type(remote_reserve_transfer_type, Some(assets_remote_reserve))?; let fees_transfer_type = - Self::check_transfer_type(fees_transfer_type, maybe_fees_remote_reserve)?; + Self::check_transfer_type(remote_reserve_transfer_type, Some(fees_remote_reserve))?; let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -324,22 +439,84 @@ where (uint8,bytes[]),\ (address,uint256)[],\ uint8,\ + uint8,\ + uint8,\ + bytes)" + )] + fn transfer_assets_using_type_and_then_address_no_reserves( + handle: &mut impl PrecompileHandle, + dest: Location, + assets: BoundedVec<(Address, Convert), GetArrayLimit>, + assets_transfer_type: u8, + remote_fees_id_index: u8, + fees_transfer_type: u8, + custom_xcm_on_dest: BoundedBytes, + ) -> EvmResult { + // Account for a possible storage read inside LocationMatcher::convert(). + // + // Storage items: AssetIdToForeignAsset (ForeignAssetCreator pallet) or AssetIdType (AssetManager pallet). + // + // Blake2_128(16) + AssetId(16) + Location + handle.record_db_read::(32 + Location::max_encoded_len())?; + handle.record_cost(1000)?; + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let assets: Vec<_> = assets.into(); + let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); + + let assets_converted = Self::convert_assets(assets)?; + let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( + assets_converted, + Some(remote_fees_id_index as usize), + )?; + let remote_fees_id = + remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + + let assets_transfer_type = Self::check_transfer_type(assets_transfer_type, None)?; + let fees_transfer_type = Self::check_transfer_type(fees_transfer_type, None)?; + + let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut custom_xcm_on_dest.as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { + dest: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets_to_send.into())), + assets_transfer_type: Box::new(assets_transfer_type), + remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), + fees_transfer_type: Box::new(fees_transfer_type), + custom_xcm_on_dest: Box::new(custom_xcm_on_dest), + weight_limit: WeightLimit::Unlimited, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + #[precompile::public( + "transferAssetsUsingTypeAndThenAddress(\ (uint8,bytes[]),\ + (address,uint256)[],\ + uint8,\ uint8,\ uint8,\ + bytes,\ (uint8,bytes[]),\ - bytes)" + bool)" )] - fn transfer_assets_using_type_and_then_address( + fn transfer_assets_using_type_and_then_address_assets_or_fee_reserve( handle: &mut impl PrecompileHandle, dest: Location, assets: BoundedVec<(Address, Convert), GetArrayLimit>, assets_transfer_type: u8, - maybe_assets_remote_reserve: Location, remote_fees_id_index: u8, fees_transfer_type: u8, - maybe_fees_remote_reserve: Location, custom_xcm_on_dest: BoundedBytes, + assets_or_fee_remote_reserve: Location, + is_assets_reserve: bool, ) -> EvmResult { // Account for a possible storage read inside LocationMatcher::convert(). // @@ -353,18 +530,93 @@ where let assets: Vec<_> = assets.into(); let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); - let assets_to_send: Vec = Self::check_and_prepare_assets(assets)?; - let remote_fees_id: AssetId = { - let asset = assets_to_send - .get(remote_fees_id_index as usize) - .ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - AssetId(asset.id.0.clone()) + let assets_converted = Self::convert_assets(assets)?; + let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( + assets_converted, + Some(remote_fees_id_index as usize), + )?; + let remote_fees_id = + remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + + let (assets_transfer_type, fees_transfer_type) = if is_assets_reserve { + ( + Self::check_transfer_type( + assets_transfer_type, + Some(assets_or_fee_remote_reserve), + )?, + Self::check_transfer_type(fees_transfer_type, None)?, + ) + } else { + ( + Self::check_transfer_type(assets_transfer_type, None)?, + Self::check_transfer_type(fees_transfer_type, Some(assets_or_fee_remote_reserve))?, + ) }; + let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( + MAX_XCM_DECODE_DEPTH, + &mut custom_xcm_on_dest.as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { + dest: Box::new(VersionedLocation::V4(dest)), + assets: Box::new(VersionedAssets::V4(assets_to_send.into())), + assets_transfer_type: Box::new(assets_transfer_type), + remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), + fees_transfer_type: Box::new(fees_transfer_type), + custom_xcm_on_dest: Box::new(custom_xcm_on_dest), + weight_limit: WeightLimit::Unlimited, + }; + + RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; + + Ok(()) + } + + #[precompile::public( + "transferAssetsUsingTypeAndThenAddress(\ + (uint8,bytes[]),\ + (address,uint256)[],\ + uint8,\ + bytes,\ + (uint8,bytes[]),\ + (uint8,bytes[]))" + )] + fn transfer_assets_using_type_and_then_address_both_reserves( + handle: &mut impl PrecompileHandle, + dest: Location, + assets: BoundedVec<(Address, Convert), GetArrayLimit>, + remote_fees_id_index: u8, + custom_xcm_on_dest: BoundedBytes, + assets_remote_reserve: Location, + fees_remote_reserve: Location, + ) -> EvmResult { + // Account for a possible storage read inside LocationMatcher::convert(). + // + // Storage items: AssetIdToForeignAsset (ForeignAssetCreator pallet) or AssetIdType (AssetManager pallet). + // + // Blake2_128(16) + AssetId(16) + Location + handle.record_db_read::(32 + Location::max_encoded_len())?; + handle.record_cost(1000)?; + + let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); + let assets: Vec<_> = assets.into(); + let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); + + let assets_converted = Self::convert_assets(assets)?; + let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( + assets_converted, + Some(remote_fees_id_index as usize), + )?; + let remote_fees_id = + remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + + let remote_reserve_transfer_type: u8 = TransferTypeHelper::RemoteReserve as u8; let assets_transfer_type = - Self::check_transfer_type(assets_transfer_type, maybe_assets_remote_reserve)?; + Self::check_transfer_type(remote_reserve_transfer_type, Some(assets_remote_reserve))?; let fees_transfer_type = - Self::check_transfer_type(fees_transfer_type, maybe_fees_remote_reserve)?; + Self::check_transfer_type(remote_reserve_transfer_type, Some(fees_remote_reserve))?; let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -388,19 +640,16 @@ where } // Helper function to convert and prepare each asset into a proper Location. - fn check_and_prepare_assets( + fn convert_assets( assets: Vec<(Address, Convert)>, - ) -> Result, PrecompileFailure> { - let mut assets_to_send: Vec = vec![]; + ) -> Result)>, PrecompileFailure> { + let mut assets_to_send: Vec<(Location, Convert)> = vec![]; for asset in assets { let asset_account = Runtime::AddressMapping::into_account_id(asset.0 .0); let asset_location = LocationMatcher::convert(asset_account); if let Some(asset_loc) = asset_location { - assets_to_send.push(Asset { - id: AssetId(asset_loc), - fun: Fungibility::Fungible(asset.1.converted()), - }) + assets_to_send.push((asset_loc, asset.1)) } else { return Err(revert("Asset not found")); } @@ -410,7 +659,7 @@ where fn check_transfer_type( transfer_type: u8, - remote_reserve: Location, + maybe_remote_reserve: Option, ) -> Result { let transfer_type_helper: TransferTypeHelper = TransferTypeHelper::decode( &mut transfer_type.to_le_bytes().as_slice(), @@ -422,10 +671,40 @@ where TransferTypeHelper::LocalReserve => return Ok(TransferType::LocalReserve), TransferTypeHelper::DestinationReserve => return Ok(TransferType::DestinationReserve), TransferTypeHelper::RemoteReserve => { - return Ok(TransferType::RemoteReserve(VersionedLocation::V4( - remote_reserve, - ))) + if let Some(remote_reserve) = maybe_remote_reserve { + return Ok(TransferType::RemoteReserve(VersionedLocation::V4( + remote_reserve, + ))); + } else { + return Err(RevertReason::custom("No reserve specified!").into()); + } } } } + + fn get_assets_to_send_and_remote_fees( + assets: Vec<(Location, Convert)>, + remote_fees_id_index: Option, + ) -> Result<(Assets, Option), PrecompileFailure> { + let assets_to_send: Assets = assets + .into_iter() + .map(|asset| Asset { + id: AssetId(asset.0), + fun: Fungibility::Fungible(asset.1.converted()), + }) + .collect::>() + .into(); + + if let Some(index) = remote_fees_id_index { + let remote_fees_id: AssetId = { + let asset = assets_to_send + .get(index) + .ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; + AssetId(asset.id.0.clone()) + }; + return Ok((assets_to_send, Some(remote_fees_id))); + } + + Ok((assets_to_send, None)) + } } diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index 27915b52..ce6c0246 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -19,7 +19,6 @@ use core::str::FromStr; use crate::{mock::*, Location}; use precompile_utils::{prelude::*, testing::*}; use sp_core::{H160, H256}; -use sp_weights::Weight; use xcm::latest::Junction::*; fn precompiles() -> Precompiles { From 396d5b203563ec66e2a4073db39de3446e517bf6 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Thu, 3 Oct 2024 21:11:08 -0700 Subject: [PATCH 05/12] remove unnecessary selectors and add a couple tests --- precompiles/pallet-xcm/XcmInterface.sol | 38 +--- precompiles/pallet-xcm/src/lib.rs | 247 ++++-------------------- precompiles/pallet-xcm/src/tests.rs | 84 +++++++- 3 files changed, 129 insertions(+), 240 deletions(-) diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index aa2df595..686fbfec 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -90,7 +90,7 @@ interface XCM { uint32 feeAssetItem ) external; - // No reserves at all + // No RemoteReserve at all function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, @@ -100,29 +100,16 @@ interface XCM { bytes memory customXcmOnDest ) external; - // Reserve for assets or fees (specified through isAssetReserve) - function transferAssetsUsingTypeAndThenLocation( - Location memory dest, - AssetLocationInfo[] memory assets, - TransferType assetsTransferType, - uint8 remoteFeesIdIndex, - TransferType feesTransferType, - bytes memory customXcmOnDest, - Location memory assetsOrFeeRemoteReserve, - bool isAssetsReserve - ) external; - - // Reserve for both assets and fees + // Reserve for assets and fees (must share same reserve if the transfer type is RemoteReserve) function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, uint8 remoteFeesIdIndex, bytes memory customXcmOnDest, - Location memory assetsRemoteReserve, - Location memory feesRemoteReserve + Location memory remoteReserve ) external; - // No reserves at all + // No RemoteReserve at all function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, @@ -132,25 +119,12 @@ interface XCM { bytes memory customXcmOnDest ) external; - // Reserve for assets or fees (specified through isAssetReserve) - function transferAssetsUsingTypeAndThenAddress( - Location memory dest, - AssetAddressInfo[] memory assets, - TransferType assetsTransferType, - uint8 remoteFeesIdIndex, - TransferType feesTransferType, - bytes memory customXcmOnDest, - Location memory assetsOrFeeRemoteReserve, - bool isAssetsReserve - ) external; - - // Reserve for both assets and fees + // Reserve for assets and fees (must share same reserve if the transfer type is RemoteReserve) function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, uint8 remoteFeesIdIndex, bytes memory customXcmOnDest, - Location memory assetsRemoteReserve, - Location memory feesRemoteReserve + Location memory remoteReserve ) external; } \ No newline at end of file diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index a9983e96..bbe1329b 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -251,7 +251,7 @@ where uint8,\ bytes)" )] - fn transfer_assets_using_type_and_then_location_no_reserves( + fn transfer_assets_using_type_and_then_location_no_remote_reserve( handle: &mut impl PrecompileHandle, dest: Location, assets: BoundedVec<(Location, Convert), GetArrayLimit>, @@ -275,8 +275,8 @@ where let remote_fees_id = remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - let assets_transfer_type = Self::check_transfer_type(assets_transfer_type, None)?; - let fees_transfer_type = Self::check_transfer_type(fees_transfer_type, None)?; + let assets_transfer_type = Self::check_transfer_type_is_not_reserve(assets_transfer_type)?; + let fees_transfer_type = Self::check_transfer_type_is_not_reserve(fees_transfer_type)?; let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -300,97 +300,21 @@ where } // Either assets reserve or fees reserve - #[precompile::public( - "transferAssetsUsingTypeAndThenLocation(\ - (uint8,bytes[]),\ - ((uint8,bytes[]),uint256)[],\ - uint8,\ - uint8,\ - uint8,\ - bytes,\ - (uint8,bytes[]),\ - bool)" - )] - fn transfer_assets_using_type_and_then_location_assets_or_fee_reserve( - handle: &mut impl PrecompileHandle, - dest: Location, - assets: BoundedVec<(Location, Convert), GetArrayLimit>, - assets_transfer_type: u8, - remote_fees_id_index: u8, - fees_transfer_type: u8, - custom_xcm_on_dest: BoundedBytes, - assets_or_fee_remote_reserve: Location, - is_assets_reserve: bool, - ) -> EvmResult { - // No DB access before try_dispatch but some logical stuff. - // To prevent spam, we charge an arbitrary amount of gas. - handle.record_cost(1000)?; - - let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let assets: Vec<_> = assets.into(); - let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); - - let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( - assets.clone(), - Some(remote_fees_id_index as usize), - )?; - let remote_fees_id = - remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - - let (assets_transfer_type, fees_transfer_type) = if is_assets_reserve { - ( - Self::check_transfer_type( - assets_transfer_type, - Some(assets_or_fee_remote_reserve), - )?, - Self::check_transfer_type(fees_transfer_type, None)?, - ) - } else { - ( - Self::check_transfer_type(assets_transfer_type, None)?, - Self::check_transfer_type(fees_transfer_type, Some(assets_or_fee_remote_reserve))?, - ) - }; - - let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( - MAX_XCM_DECODE_DEPTH, - &mut custom_xcm_on_dest.as_slice(), - ) - .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; - - let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { - dest: Box::new(VersionedLocation::V4(dest)), - assets: Box::new(VersionedAssets::V4(assets_to_send)), - assets_transfer_type: Box::new(assets_transfer_type), - remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), - fees_transfer_type: Box::new(fees_transfer_type), - custom_xcm_on_dest: Box::new(custom_xcm_on_dest), - weight_limit: WeightLimit::Unlimited, - }; - - RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - - Ok(()) - } - - // Both reserves for assets and fees #[precompile::public( "transferAssetsUsingTypeAndThenLocation(\ (uint8,bytes[]),\ ((uint8,bytes[]),uint256)[],\ uint8,\ bytes,\ - (uint8,bytes[]),\ (uint8,bytes[]))" )] - fn transfer_assets_using_type_and_then_location_both_reserves( + fn transfer_assets_using_type_and_then_location_remote_reserve( handle: &mut impl PrecompileHandle, dest: Location, assets: BoundedVec<(Location, Convert), GetArrayLimit>, remote_fees_id_index: u8, custom_xcm_on_dest: BoundedBytes, - assets_remote_reserve: Location, - fees_remote_reserve: Location, + remote_reserve: Location, ) -> EvmResult { // No DB access before try_dispatch but some logical stuff. // To prevent spam, we charge an arbitrary amount of gas. @@ -407,24 +331,21 @@ where let remote_fees_id = remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - let remote_reserve_transfer_type: u8 = TransferTypeHelper::RemoteReserve as u8; - let assets_transfer_type = - Self::check_transfer_type(remote_reserve_transfer_type, Some(assets_remote_reserve))?; - let fees_transfer_type = - Self::check_transfer_type(remote_reserve_transfer_type, Some(fees_remote_reserve))?; - let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut custom_xcm_on_dest.as_slice(), ) .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + let asset_and_fees_transfer_type = + TransferType::RemoteReserve(VersionedLocation::V4(remote_reserve)); + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { dest: Box::new(VersionedLocation::V4(dest)), assets: Box::new(VersionedAssets::V4(assets_to_send)), - assets_transfer_type: Box::new(assets_transfer_type), + assets_transfer_type: Box::new(asset_and_fees_transfer_type.clone()), remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), - fees_transfer_type: Box::new(fees_transfer_type), + fees_transfer_type: Box::new(asset_and_fees_transfer_type), custom_xcm_on_dest: Box::new(custom_xcm_on_dest), weight_limit: WeightLimit::Unlimited, }; @@ -443,7 +364,7 @@ where uint8,\ bytes)" )] - fn transfer_assets_using_type_and_then_address_no_reserves( + fn transfer_assets_using_type_and_then_address_no_remote_reserve( handle: &mut impl PrecompileHandle, dest: Location, assets: BoundedVec<(Address, Convert), GetArrayLimit>, @@ -472,8 +393,8 @@ where let remote_fees_id = remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - let assets_transfer_type = Self::check_transfer_type(assets_transfer_type, None)?; - let fees_transfer_type = Self::check_transfer_type(fees_transfer_type, None)?; + let assets_transfer_type = Self::check_transfer_type_is_not_reserve(assets_transfer_type)?; + let fees_transfer_type = Self::check_transfer_type_is_not_reserve(fees_transfer_type)?; let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -501,96 +422,16 @@ where (uint8,bytes[]),\ (address,uint256)[],\ uint8,\ - uint8,\ - uint8,\ bytes,\ - (uint8,bytes[]),\ - bool)" - )] - fn transfer_assets_using_type_and_then_address_assets_or_fee_reserve( - handle: &mut impl PrecompileHandle, - dest: Location, - assets: BoundedVec<(Address, Convert), GetArrayLimit>, - assets_transfer_type: u8, - remote_fees_id_index: u8, - fees_transfer_type: u8, - custom_xcm_on_dest: BoundedBytes, - assets_or_fee_remote_reserve: Location, - is_assets_reserve: bool, - ) -> EvmResult { - // Account for a possible storage read inside LocationMatcher::convert(). - // - // Storage items: AssetIdToForeignAsset (ForeignAssetCreator pallet) or AssetIdType (AssetManager pallet). - // - // Blake2_128(16) + AssetId(16) + Location - handle.record_db_read::(32 + Location::max_encoded_len())?; - handle.record_cost(1000)?; - - let origin = Runtime::AddressMapping::into_account_id(handle.context().caller); - let assets: Vec<_> = assets.into(); - let custom_xcm_on_dest: Vec = custom_xcm_on_dest.into(); - - let assets_converted = Self::convert_assets(assets)?; - let (assets_to_send, remote_fees_id) = Self::get_assets_to_send_and_remote_fees( - assets_converted, - Some(remote_fees_id_index as usize), - )?; - let remote_fees_id = - remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - - let (assets_transfer_type, fees_transfer_type) = if is_assets_reserve { - ( - Self::check_transfer_type( - assets_transfer_type, - Some(assets_or_fee_remote_reserve), - )?, - Self::check_transfer_type(fees_transfer_type, None)?, - ) - } else { - ( - Self::check_transfer_type(assets_transfer_type, None)?, - Self::check_transfer_type(fees_transfer_type, Some(assets_or_fee_remote_reserve))?, - ) - }; - - let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( - MAX_XCM_DECODE_DEPTH, - &mut custom_xcm_on_dest.as_slice(), - ) - .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; - - let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { - dest: Box::new(VersionedLocation::V4(dest)), - assets: Box::new(VersionedAssets::V4(assets_to_send.into())), - assets_transfer_type: Box::new(assets_transfer_type), - remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), - fees_transfer_type: Box::new(fees_transfer_type), - custom_xcm_on_dest: Box::new(custom_xcm_on_dest), - weight_limit: WeightLimit::Unlimited, - }; - - RuntimeHelper::::try_dispatch(handle, Some(origin).into(), call)?; - - Ok(()) - } - - #[precompile::public( - "transferAssetsUsingTypeAndThenAddress(\ - (uint8,bytes[]),\ - (address,uint256)[],\ - uint8,\ - bytes,\ - (uint8,bytes[]),\ (uint8,bytes[]))" )] - fn transfer_assets_using_type_and_then_address_both_reserves( + fn transfer_assets_using_type_and_then_address_remote_reserve( handle: &mut impl PrecompileHandle, dest: Location, assets: BoundedVec<(Address, Convert), GetArrayLimit>, remote_fees_id_index: u8, custom_xcm_on_dest: BoundedBytes, - assets_remote_reserve: Location, - fees_remote_reserve: Location, + remote_reserve: Location, ) -> EvmResult { // Account for a possible storage read inside LocationMatcher::convert(). // @@ -612,24 +453,21 @@ where let remote_fees_id = remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - let remote_reserve_transfer_type: u8 = TransferTypeHelper::RemoteReserve as u8; - let assets_transfer_type = - Self::check_transfer_type(remote_reserve_transfer_type, Some(assets_remote_reserve))?; - let fees_transfer_type = - Self::check_transfer_type(remote_reserve_transfer_type, Some(fees_remote_reserve))?; - let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, &mut custom_xcm_on_dest.as_slice(), ) .map_err(|_| RevertReason::custom("Failed decoding custom XCM message"))?; + let asset_and_fees_transfer_type = + TransferType::RemoteReserve(VersionedLocation::V4(remote_reserve)); + let call = pallet_xcm::Call::::transfer_assets_using_type_and_then { dest: Box::new(VersionedLocation::V4(dest)), assets: Box::new(VersionedAssets::V4(assets_to_send.into())), - assets_transfer_type: Box::new(assets_transfer_type), + assets_transfer_type: Box::new(asset_and_fees_transfer_type.clone()), remote_fees_id: Box::new(VersionedAssetId::V4(remote_fees_id)), - fees_transfer_type: Box::new(fees_transfer_type), + fees_transfer_type: Box::new(asset_and_fees_transfer_type), custom_xcm_on_dest: Box::new(custom_xcm_on_dest), weight_limit: WeightLimit::Unlimited, }; @@ -657,31 +495,6 @@ where Ok(assets_to_send) } - fn check_transfer_type( - transfer_type: u8, - maybe_remote_reserve: Option, - ) -> Result { - let transfer_type_helper: TransferTypeHelper = TransferTypeHelper::decode( - &mut transfer_type.to_le_bytes().as_slice(), - ) - .map_err(|_| RevertReason::custom("Failed decoding value for TransferTypeHelper"))?; - - match transfer_type_helper { - TransferTypeHelper::Teleport => return Ok(TransferType::Teleport), - TransferTypeHelper::LocalReserve => return Ok(TransferType::LocalReserve), - TransferTypeHelper::DestinationReserve => return Ok(TransferType::DestinationReserve), - TransferTypeHelper::RemoteReserve => { - if let Some(remote_reserve) = maybe_remote_reserve { - return Ok(TransferType::RemoteReserve(VersionedLocation::V4( - remote_reserve, - ))); - } else { - return Err(RevertReason::custom("No reserve specified!").into()); - } - } - } - } - fn get_assets_to_send_and_remote_fees( assets: Vec<(Location, Convert)>, remote_fees_id_index: Option, @@ -707,4 +520,24 @@ where Ok((assets_to_send, None)) } + + fn check_transfer_type_is_not_reserve( + transfer_type: u8, + ) -> Result { + let transfer_type_helper: TransferTypeHelper = TransferTypeHelper::decode( + &mut transfer_type.to_le_bytes().as_slice(), + ) + .map_err(|_| RevertReason::custom("Failed decoding value for TransferTypeHelper"))?; + + match transfer_type_helper { + TransferTypeHelper::Teleport => return Ok(TransferType::Teleport), + TransferTypeHelper::LocalReserve => return Ok(TransferType::LocalReserve), + TransferTypeHelper::DestinationReserve => return Ok(TransferType::DestinationReserve), + TransferTypeHelper::RemoteReserve => { + return Err( + RevertReason::custom("RemoteReserve not allowed for this method!").into(), + ) + } + } + } } diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index ce6c0246..97e35ff7 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -16,10 +16,12 @@ use core::str::FromStr; -use crate::{mock::*, Location}; +use crate::{mock::*, Location, TransferTypeHelper}; +use parity_scale_codec::Encode; use precompile_utils::{prelude::*, testing::*}; use sp_core::{H160, H256}; use xcm::latest::Junction::*; +use xcm::prelude::*; fn precompiles() -> Precompiles { PrecompilesValue::get() @@ -393,3 +395,83 @@ fn test_transfer_assets_to_relay_foreign_asset() { .execute_returns(()); }); } + +#[test] +fn test_transfer_assets_using_type_and_then_location_no_remote_reserve() { + ExtBuilder::default() + .with_balances(vec![(Alice.into(), 1000)]) + .with_xcm_assets(vec![XcmAssetDetails { + location: Location::parent(), + admin: Alice.into(), + asset_id: 5u16, + is_sufficient: true, + balance_to_mint: 10000u128, + min_balance: 1u128, + }]) + .build() + .execute_with(|| { + let dest = Location::new(1, [Parachain(2)]); + let destination_asset_location = Location::new(1, [Parachain(2), PalletInstance(3)]); + let origin_asset_location = Location::new(0, [PalletInstance(1)]); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + precompiles() + .prepare_test( + Alice, + Precompile1, + PCall::transfer_assets_using_type_and_then_location_no_remote_reserve { + dest, + assets: vec![ + (origin_asset_location, 100u128.into()), + (destination_asset_location, 150u128.into()), + ] + .into(), + assets_transfer_type: TransferTypeHelper::DestinationReserve as u8, + remote_fees_id_index: 0u8, + fees_transfer_type: TransferTypeHelper::LocalReserve as u8, + custom_xcm_on_dest: message.into(), + }, + ) + .expect_cost(100001001) + .expect_no_logs() + .execute_returns(()); + }); +} + +#[test] +fn test_transfer_assets_using_type_and_then_location_remote_reserve() { + ExtBuilder::default() + .with_balances(vec![(Alice.into(), 1000)]) + .with_xcm_assets(vec![XcmAssetDetails { + location: Location::parent(), + admin: Alice.into(), + asset_id: 5u16, + is_sufficient: true, + balance_to_mint: 10000u128, + min_balance: 1u128, + }]) + .build() + .execute_with(|| { + let dest = Location::new(1, [Parachain(2)]); + let relay_asset_location = Location::parent(); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + precompiles() + .prepare_test( + Alice, + Precompile1, + PCall::transfer_assets_using_type_and_then_location_remote_reserve { + dest, + assets: vec![(relay_asset_location, 150u128.into())].into(), + remote_fees_id_index: 0u8, + custom_xcm_on_dest: message.into(), + remote_reserve: Location::parent(), + }, + ) + .expect_cost(100001001) + .expect_no_logs() + .execute_returns(()); + }); +} From c8202b8cb444cd6900391f47a6a677a840b042d7 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sat, 5 Oct 2024 11:15:17 -0700 Subject: [PATCH 06/12] add more tests --- precompiles/pallet-xcm/src/tests.rs | 87 +++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index 97e35ff7..33e83e68 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -475,3 +475,90 @@ fn test_transfer_assets_using_type_and_then_location_remote_reserve() { .execute_returns(()); }); } + +#[test] +fn test_transfer_assets_using_type_and_then_address_no_remote_reserve() { + ExtBuilder::default() + .with_balances(vec![(Alice.into(), 1000)]) + .with_xcm_assets(vec![XcmAssetDetails { + location: Location::parent(), + admin: Alice.into(), + asset_id: 5u16, + is_sufficient: true, + balance_to_mint: 10000u128, + min_balance: 1u128, + }]) + .build() + .execute_with(|| { + // Foreign (relay) asset with prefix [255; 18] and assetId of 5u16. + let asset_address = + H160::from_str("0xfFfFFFffFffFFFFffFFfFfffFfFFFFFfffFF0005").unwrap(); + + // We send the native currency of the origin chain and pay fees with it. + let pallet_balances_address = H160::from_low_u64_be(2050); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + precompiles() + .prepare_test( + Alice, + Precompile1, + PCall::transfer_assets_using_type_and_then_address_no_remote_reserve { + dest: Location::parent(), + assets: vec![ + (Address(pallet_balances_address), 500.into()), + (Address(asset_address), 500.into()), + ] + .into(), + assets_transfer_type: TransferTypeHelper::DestinationReserve as u8, + remote_fees_id_index: 0u8, + fees_transfer_type: TransferTypeHelper::LocalReserve as u8, + custom_xcm_on_dest: message.into(), + }, + ) + .expect_cost(100001002) + .expect_no_logs() + .execute_returns(()); + }); +} + +#[test] +fn test_transfer_assets_using_type_and_then_address_remote_reserve() { + ExtBuilder::default() + .with_balances(vec![(Alice.into(), 1000)]) + .with_xcm_assets(vec![XcmAssetDetails { + location: Location::parent(), + admin: Alice.into(), + asset_id: 5u16, + is_sufficient: true, + balance_to_mint: 10000u128, + min_balance: 1u128, + }]) + .build() + .execute_with(|| { + // Foreign (relay) asset with prefix [255; 18] and assetId of 5u16. + let asset_address = + H160::from_str("0xfFfFFFffFffFFFFffFFfFfffFfFFFFFfffFF0005").unwrap(); + + let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); + + precompiles() + .prepare_test( + Alice, + Precompile1, + PCall::transfer_assets_using_type_and_then_address_remote_reserve { + dest: Location::parent(), + assets: vec![ + (Address(asset_address), 500.into()), + ] + .into(), + remote_fees_id_index: 0u8, + custom_xcm_on_dest: message.into(), + remote_reserve: Location::parent() + }, + ) + .expect_cost(100001002) + .expect_no_logs() + .execute_returns(()); + }); +} \ No newline at end of file From aea402dd3193edd09242b0be19e07e3da4e07200 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sat, 5 Oct 2024 11:19:26 -0700 Subject: [PATCH 07/12] pr suggestions --- precompiles/pallet-xcm/XcmInterface.sol | 7 +++---- precompiles/pallet-xcm/src/lib.rs | 18 +++++------------- precompiles/pallet-xcm/src/tests.rs | 11 ++++------- 3 files changed, 12 insertions(+), 24 deletions(-) diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index 686fbfec..daa0222b 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -33,8 +33,7 @@ interface XCM { enum TransferType { Teleport, LocalReserve, - DestinationReserve, - RemoteReserve + DestinationReserve } /// @dev Function to send assets via XCM using transfer_assets() pallet-xcm extrinsic. @@ -100,7 +99,7 @@ interface XCM { bytes memory customXcmOnDest ) external; - // Reserve for assets and fees (must share same reserve if the transfer type is RemoteReserve) + // Remote reserve for assets and fees (must share same remote reserve) function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, @@ -119,7 +118,7 @@ interface XCM { bytes memory customXcmOnDest ) external; - // Reserve for assets and fees (must share same reserve if the transfer type is RemoteReserve) + // Remote reserve for assets and fees (must share same reserve) function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index bbe1329b..28f9712a 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -60,7 +60,6 @@ pub enum TransferTypeHelper { Teleport = 0, LocalReserve = 1, DestinationReserve = 2, - RemoteReserve = 3, } pub struct PalletXcmPrecompile(PhantomData<(Runtime, LocationMatcher)>); @@ -275,8 +274,8 @@ where let remote_fees_id = remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - let assets_transfer_type = Self::check_transfer_type_is_not_reserve(assets_transfer_type)?; - let fees_transfer_type = Self::check_transfer_type_is_not_reserve(fees_transfer_type)?; + let assets_transfer_type = Self::parse_transfer_type(assets_transfer_type)?; + let fees_transfer_type = Self::parse_transfer_type(fees_transfer_type)?; let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -393,8 +392,8 @@ where let remote_fees_id = remote_fees_id.ok_or_else(|| RevertReason::custom("remote_fees_id not found"))?; - let assets_transfer_type = Self::check_transfer_type_is_not_reserve(assets_transfer_type)?; - let fees_transfer_type = Self::check_transfer_type_is_not_reserve(fees_transfer_type)?; + let assets_transfer_type = Self::parse_transfer_type(assets_transfer_type)?; + let fees_transfer_type = Self::parse_transfer_type(fees_transfer_type)?; let custom_xcm_on_dest = VersionedXcm::<()>::decode_all_with_depth_limit( MAX_XCM_DECODE_DEPTH, @@ -521,9 +520,7 @@ where Ok((assets_to_send, None)) } - fn check_transfer_type_is_not_reserve( - transfer_type: u8, - ) -> Result { + fn parse_transfer_type(transfer_type: u8) -> Result { let transfer_type_helper: TransferTypeHelper = TransferTypeHelper::decode( &mut transfer_type.to_le_bytes().as_slice(), ) @@ -533,11 +530,6 @@ where TransferTypeHelper::Teleport => return Ok(TransferType::Teleport), TransferTypeHelper::LocalReserve => return Ok(TransferType::LocalReserve), TransferTypeHelper::DestinationReserve => return Ok(TransferType::DestinationReserve), - TransferTypeHelper::RemoteReserve => { - return Err( - RevertReason::custom("RemoteReserve not allowed for this method!").into(), - ) - } } } } diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index 33e83e68..a3ad05f7 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -505,7 +505,7 @@ fn test_transfer_assets_using_type_and_then_address_no_remote_reserve() { Precompile1, PCall::transfer_assets_using_type_and_then_address_no_remote_reserve { dest: Location::parent(), - assets: vec![ + assets: vec![ (Address(pallet_balances_address), 500.into()), (Address(asset_address), 500.into()), ] @@ -548,17 +548,14 @@ fn test_transfer_assets_using_type_and_then_address_remote_reserve() { Precompile1, PCall::transfer_assets_using_type_and_then_address_remote_reserve { dest: Location::parent(), - assets: vec![ - (Address(asset_address), 500.into()), - ] - .into(), + assets: vec![(Address(asset_address), 500.into())].into(), remote_fees_id_index: 0u8, custom_xcm_on_dest: message.into(), - remote_reserve: Location::parent() + remote_reserve: Location::parent(), }, ) .expect_cost(100001002) .expect_no_logs() .execute_returns(()); }); -} \ No newline at end of file +} From 92b18fa359ea407c82098164bf804649a1f5f317 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sat, 5 Oct 2024 11:51:25 -0700 Subject: [PATCH 08/12] add documentation for solidity selectors --- precompiles/pallet-xcm/XcmInterface.sol | 54 +++++++++++++++++++++---- precompiles/pallet-xcm/src/tests.rs | 4 ++ 2 files changed, 50 insertions(+), 8 deletions(-) diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index daa0222b..cf20f066 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -40,7 +40,7 @@ interface XCM { /// @custom:selector 9ea8ada7 /// @param dest The destination chain. /// @param beneficiary The actual account that will receive the tokens on dest. - /// @param assets The combination (array) of assets to send. + /// @param assets The combination (array) of assets to send in Location format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsLocation( Location memory dest, @@ -54,7 +54,7 @@ interface XCM { /// @custom:selector a0aeb5fe /// @param paraId The para-id of the destination chain. /// @param beneficiary The actual account that will receive the tokens on paraId destination. - /// @param assets The combination (array) of assets to send. + /// @param assets The combination (array) of assets to send in Address format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsToPara20( uint32 paraId, @@ -68,7 +68,7 @@ interface XCM { /// @custom:selector f23032c3 /// @param paraId The para-id of the destination chain. /// @param beneficiary The actual account that will receive the tokens on paraId destination. - /// @param assets The combination (array) of assets to send. + /// @param assets The combination (array) of assets to send in Address format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsToPara32( uint32 paraId, @@ -81,7 +81,7 @@ interface XCM { /// using transfer_assets() pallet-xcm extrinsic. /// @custom:selector 6521cc2c /// @param beneficiary The actual account that will receive the tokens on the relay chain. - /// @param assets The combination (array) of assets to send. + /// @param assets The combination (array) of assets to send in Address format. /// @param feeAssetItem The index of the asset that will be used to pay for fees. function transferAssetsToRelay( bytes32 beneficiary, @@ -89,7 +89,18 @@ interface XCM { uint32 feeAssetItem ) external; - // No RemoteReserve at all + /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm + /// extrinsic. + /// Important: in this selector RemoteReserve type (for either assets or fees) is not allowed. + /// If users want to send assets and fees (in Location format) with a remote reserve, + /// they must use the selector fc19376c. + /// @custom:selector 8425d893 + /// @param dest The destination chain. + /// @param assets The combination (array) of assets to send in Location format. + /// @param assetsTransferType The TransferType corresponding to assets being sent. + /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. + /// @param feesTransferType The TransferType corresponding to the asset used as fees. + /// @param customXcmOnDest The XCM message to execute on destination chain. function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, @@ -99,7 +110,15 @@ interface XCM { bytes memory customXcmOnDest ) external; - // Remote reserve for assets and fees (must share same remote reserve) + /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm + /// extrinsic. + /// @custom:selector fc19376c + /// @param dest The destination chain. + /// @param assets The combination (array) of assets to send in Location format. + /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. + /// @param customXcmOnDest The XCM message to execute on destination chain. + /// @param remoteReserve The remote reserve corresponding for assets and fees. They MUST + /// share the same reserve. function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, @@ -108,7 +127,18 @@ interface XCM { Location memory remoteReserve ) external; - // No RemoteReserve at all + /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm + /// extrinsic. + /// Important: in this selector RemoteReserve type (for either assets or fees) is not allowed. + /// If users want to send assets and fees (in Address format) with a remote reserve, + /// they must use the selector aaecfc62. + /// @custom:selector 998093ee + /// @param dest The destination chain. + /// @param assets The combination (array) of assets to send in Address format. + /// @param assetsTransferType The TransferType corresponding to assets being sent. + /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. + /// @param feesTransferType The TransferType corresponding to the asset used as fees. + /// @param customXcmOnDest The XCM message to execute on destination chain. function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, @@ -118,7 +148,15 @@ interface XCM { bytes memory customXcmOnDest ) external; - // Remote reserve for assets and fees (must share same reserve) + /// @dev Function to send assets through transfer_assets_using_type_and_then() pallet-xcm + /// extrinsic. + /// @custom:selector aaecfc62 + /// @param dest The destination chain. + /// @param assets The combination (array) of assets to send in Address format. + /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. + /// @param customXcmOnDest The XCM message to execute on destination chain. + /// @param remoteReserve The remote reserve corresponding for assets and fees. They MUST + /// share the same reserve. function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index a3ad05f7..82b1d99d 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -38,6 +38,10 @@ fn selectors() { assert!(PCall::transfer_assets_to_para_20_selectors().contains(&0xa0aeb5fe)); assert!(PCall::transfer_assets_to_para_32_selectors().contains(&0xf23032c3)); assert!(PCall::transfer_assets_to_relay_selectors().contains(&0x6521cc2c)); + assert!(PCall::transfer_assets_using_type_and_then_location_no_remote_reserve_selectors().contains(&0x8425d893)); + assert!(PCall::transfer_assets_using_type_and_then_location_remote_reserve_selectors().contains(&0xfc19376c)); + assert!(PCall::transfer_assets_using_type_and_then_address_no_remote_reserve_selectors().contains(&0x998093ee)); + assert!(PCall::transfer_assets_using_type_and_then_address_remote_reserve_selectors().contains(&0xaaecfc62)); } #[test] From e7b94384a12aa75511f950ff2b17296f9d5ef49d Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sat, 5 Oct 2024 11:58:04 -0700 Subject: [PATCH 09/12] fmt --- precompiles/pallet-xcm/src/lib.rs | 2 -- precompiles/pallet-xcm/src/tests.rs | 20 ++++++++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/precompiles/pallet-xcm/src/lib.rs b/precompiles/pallet-xcm/src/lib.rs index 28f9712a..f067142b 100644 --- a/precompiles/pallet-xcm/src/lib.rs +++ b/precompiles/pallet-xcm/src/lib.rs @@ -240,7 +240,6 @@ where Ok(()) } - // First selector type: no reserves at all #[precompile::public( "transferAssetsUsingTypeAndThenLocation(\ (uint8,bytes[]),\ @@ -298,7 +297,6 @@ where Ok(()) } - // Either assets reserve or fees reserve #[precompile::public( "transferAssetsUsingTypeAndThenLocation(\ (uint8,bytes[]),\ diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index 82b1d99d..0c167bbf 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -38,10 +38,22 @@ fn selectors() { assert!(PCall::transfer_assets_to_para_20_selectors().contains(&0xa0aeb5fe)); assert!(PCall::transfer_assets_to_para_32_selectors().contains(&0xf23032c3)); assert!(PCall::transfer_assets_to_relay_selectors().contains(&0x6521cc2c)); - assert!(PCall::transfer_assets_using_type_and_then_location_no_remote_reserve_selectors().contains(&0x8425d893)); - assert!(PCall::transfer_assets_using_type_and_then_location_remote_reserve_selectors().contains(&0xfc19376c)); - assert!(PCall::transfer_assets_using_type_and_then_address_no_remote_reserve_selectors().contains(&0x998093ee)); - assert!(PCall::transfer_assets_using_type_and_then_address_remote_reserve_selectors().contains(&0xaaecfc62)); + assert!( + PCall::transfer_assets_using_type_and_then_location_no_remote_reserve_selectors() + .contains(&0x8425d893) + ); + assert!( + PCall::transfer_assets_using_type_and_then_location_remote_reserve_selectors() + .contains(&0xfc19376c) + ); + assert!( + PCall::transfer_assets_using_type_and_then_address_no_remote_reserve_selectors() + .contains(&0x998093ee) + ); + assert!( + PCall::transfer_assets_using_type_and_then_address_remote_reserve_selectors() + .contains(&0xaaecfc62) + ); } #[test] From 6ef17ec7acab9dc2ade3f99eb6c8b8a8eeb7d8f3 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sat, 5 Oct 2024 12:42:00 -0700 Subject: [PATCH 10/12] fix test --- precompiles/pallet-xcm/src/tests.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/precompiles/pallet-xcm/src/tests.rs b/precompiles/pallet-xcm/src/tests.rs index 0c167bbf..91508c43 100644 --- a/precompiles/pallet-xcm/src/tests.rs +++ b/precompiles/pallet-xcm/src/tests.rs @@ -556,6 +556,7 @@ fn test_transfer_assets_using_type_and_then_address_remote_reserve() { let asset_address = H160::from_str("0xfFfFFFffFffFFFFffFFfFfffFfFFFFFfffFF0005").unwrap(); + let dest = Location::new(1, [Parachain(2)]); let message: Vec = xcm::VersionedXcm::<()>::V4(Xcm(vec![ClearOrigin])).encode(); precompiles() @@ -563,7 +564,7 @@ fn test_transfer_assets_using_type_and_then_address_remote_reserve() { Alice, Precompile1, PCall::transfer_assets_using_type_and_then_address_remote_reserve { - dest: Location::parent(), + dest, assets: vec![(Address(asset_address), 500.into())].into(), remote_fees_id_index: 0u8, custom_xcm_on_dest: message.into(), From 205e2d6533dae81d365acbdb53f0b5e5efe13e4d Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Sat, 5 Oct 2024 12:42:15 -0700 Subject: [PATCH 11/12] fix rust tests compilation --- pallets/emergency-para-xcm/src/mock.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/pallets/emergency-para-xcm/src/mock.rs b/pallets/emergency-para-xcm/src/mock.rs index f742961e..e9a7ea9f 100644 --- a/pallets/emergency-para-xcm/src/mock.rs +++ b/pallets/emergency-para-xcm/src/mock.rs @@ -19,6 +19,7 @@ use crate as pallet_emergency_para_xcm; use cumulus_pallet_parachain_system::ParachainSetCode; use cumulus_primitives_core::{ relay_chain::BlockNumber as RelayBlockNumber, AggregateMessageOrigin, ParaId, + XcmpMessageHandler, }; use frame_support::parameter_types; use frame_support::traits::ConstU32; From 09d22fc6e7643944871f4bdf68f0bac162ecc2d8 Mon Sep 17 00:00:00 2001 From: Agusrodri Date: Mon, 7 Oct 2024 05:36:16 -0700 Subject: [PATCH 12/12] add clarification for SCALE encoding in custom xcm --- precompiles/pallet-xcm/XcmInterface.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/precompiles/pallet-xcm/XcmInterface.sol b/precompiles/pallet-xcm/XcmInterface.sol index cf20f066..d657a2b9 100644 --- a/precompiles/pallet-xcm/XcmInterface.sol +++ b/precompiles/pallet-xcm/XcmInterface.sol @@ -100,7 +100,7 @@ interface XCM { /// @param assetsTransferType The TransferType corresponding to assets being sent. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. /// @param feesTransferType The TransferType corresponding to the asset used as fees. - /// @param customXcmOnDest The XCM message to execute on destination chain. + /// @param customXcmOnDest The XCM message to execute on destination chain (SCALE encoded). function transferAssetsUsingTypeAndThenLocation( Location memory dest, AssetLocationInfo[] memory assets, @@ -116,7 +116,7 @@ interface XCM { /// @param dest The destination chain. /// @param assets The combination (array) of assets to send in Location format. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. - /// @param customXcmOnDest The XCM message to execute on destination chain. + /// @param customXcmOnDest The XCM message to execute on destination chain (SCALE encoded). /// @param remoteReserve The remote reserve corresponding for assets and fees. They MUST /// share the same reserve. function transferAssetsUsingTypeAndThenLocation( @@ -138,7 +138,7 @@ interface XCM { /// @param assetsTransferType The TransferType corresponding to assets being sent. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. /// @param feesTransferType The TransferType corresponding to the asset used as fees. - /// @param customXcmOnDest The XCM message to execute on destination chain. + /// @param customXcmOnDest The XCM message to execute on destination chain (SCALE encoded). function transferAssetsUsingTypeAndThenAddress( Location memory dest, AssetAddressInfo[] memory assets, @@ -154,7 +154,7 @@ interface XCM { /// @param dest The destination chain. /// @param assets The combination (array) of assets to send in Address format. /// @param remoteFeesIdIndex The index of the asset (inside assets array) to use as fees. - /// @param customXcmOnDest The XCM message to execute on destination chain. + /// @param customXcmOnDest The XCM message to execute on destination chain (SCALE encoded). /// @param remoteReserve The remote reserve corresponding for assets and fees. They MUST /// share the same reserve. function transferAssetsUsingTypeAndThenAddress(