Skip to content

Commit

Permalink
add new functions
Browse files Browse the repository at this point in the history
  • Loading branch information
Agusrodri committed Apr 16, 2024
1 parent be1291b commit c048e7c
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 16 deletions.
42 changes: 38 additions & 4 deletions precompiles/pallet-xcm/XcmInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,23 +18,57 @@ interface XCM {
}

// A way to represent fungible assets in XCM
struct Asset {
struct AssetLocationInfo {
Location location;
uint256 amount;
}

struct AssetAddressInfo {
address asset;
uint256 amount;
}

/// @dev Function to send assets via XCM using transfer_assets() pallet-xcm extrinsic.
/// @custom:selector 650ef8c7
/// @custom:selector 59df8416
/// @param dest The destination chain.
/// @param beneficiary The actual account that will receive the tokens in 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 transferAssets(
function transferAssetsLocation(
Location memory dest,
Location memory beneficiary,
Asset[] memory assets,
AssetLocationInfo[] memory assets,
uint32 feeAssetItem,
Weight memory weight
) external;

/// TODO add docs
/// @custom:selector b489262e
function transferAssetsToPara20(
uint32 paraId,
address beneficiary,
AssetAddressInfo[] memory assets,
uint32 feeAssetItem,
Weight memory weight
) external;

/// TODO add docs
/// @custom:selector 4461e6f5
function transferAssetsToPara32(
uint32 paraId,
bytes32 beneficiary,
AssetAddressInfo[] memory assets,
uint32 feeAssetItem,
Weight memory weight
) external;

/// TODO add docs
/// @custom:selector d7c89659
function transferAssetsToRelay(
bytes32 beneficiary,
AssetAddressInfo[] memory assets,
uint32 feeAssetItem,
Weight memory weight
) external;
Expand Down
165 changes: 160 additions & 5 deletions precompiles/pallet-xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@

#![cfg_attr(not(feature = "std"), no_std)]

use fp_evm::PrecompileHandle;
use fp_evm::{PrecompileFailure, PrecompileHandle};
use frame_support::{
dispatch::{GetDispatchInfo, PostDispatchInfo},
traits::ConstU32,
};
use pallet_evm::AddressMapping;
use precompile_utils::prelude::*;

use sp_core::U256;
use sp_core::{H256, U256};
use sp_runtime::traits::Dispatchable;
use sp_std::marker::PhantomData;
use sp_weights::Weight;
Expand All @@ -33,7 +33,13 @@ use xcm::{
prelude::WeightLimit::*,
VersionedAssets, VersionedLocation,
};
use xcm_primitives::location_converter::AccountIdToLocationMatcher;
use xcm_primitives::{
generators::{
XcmLocalBeneficiary20Generator, XcmLocalBeneficiary32Generator,
XcmSiblingDestinationGenerator,
},
location_matcher::AccountIdToLocationMatcher,
};

#[cfg(test)]
mod mock;
Expand All @@ -57,14 +63,14 @@ where
LocationMatcher: AccountIdToLocationMatcher<<Runtime as frame_system::Config>::AccountId>,
{
#[precompile::public(
"transferAssets(\
"transferAssetsLocation(\
(uint8,bytes[]),\
(uint8,bytes[]),\
((uint8,bytes[]),uint256)[],\
uint32,\
(uint64,uint64))"
)]
fn transfer_assets(
fn transfer_assets_location(
handle: &mut impl PrecompileHandle,
dest: Location,
beneficiary: Location,
Expand Down Expand Up @@ -104,4 +110,153 @@ where
RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;
Ok(())
}

#[precompile::public(
"transferAssetsToPara20(\
uint32,\
address,\
(address,uint256)[],\
uint32,\
(uint64,uint64))"
)]
fn transfer_assets_to_para_20(
handle: &mut impl PrecompileHandle,
para_id: u32,
beneficiary: Address,
assets: BoundedVec<(Address, Convert<U256, u128>), GetArrayLimit>,
fee_asset_item: u32,
weight: Weight,
) -> EvmResult {
// TODO: account for a possible storage read inside LocationMatcher::convert()

let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let assets: Vec<_> = assets.into();

let assets_to_send: Vec<Asset> = 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);

let call = pallet_xcm::Call::<Runtime>::transfer_assets {
dest: Box::new(VersionedLocation::V4(dest)),
beneficiary: Box::new(VersionedLocation::V4(beneficiary)),
assets: Box::new(VersionedAssets::V4(assets_to_send.into())),
fee_asset_item,
weight_limit,
};

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

Ok(())
}

#[precompile::public(
"transferAssetsToPara32(\
uint32,\
bytes32,\
(address,uint256)[],\
uint32,\
(uint64,uint64))"
)]
fn transfer_assets_to_para_32(
handle: &mut impl PrecompileHandle,
para_id: u32,
beneficiary: H256,
assets: BoundedVec<(Address, Convert<U256, u128>), GetArrayLimit>,
fee_asset_item: u32,
weight: Weight,
) -> EvmResult {
// TODO: account for a possible storage read inside LocationMatcher::convert()

let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let assets: Vec<_> = assets.into();

let assets_to_send: Vec<Asset> = 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);

let call = pallet_xcm::Call::<Runtime>::transfer_assets {
dest: Box::new(VersionedLocation::V4(dest)),
beneficiary: Box::new(VersionedLocation::V4(beneficiary)),
assets: Box::new(VersionedAssets::V4(assets_to_send.into())),
fee_asset_item,
weight_limit,
};

RuntimeHelper::<Runtime>::try_dispatch(handle, Some(origin).into(), call)?;

Ok(())
}

#[precompile::public(
"transferAssetsToRelay(\
bytes32,\
(address,uint256)[],\
uint32,\
(uint64,uint64))"
)]
fn transfer_assets_to_relay(
handle: &mut impl PrecompileHandle,
beneficiary: H256,
assets: BoundedVec<(Address, Convert<U256, u128>), GetArrayLimit>,
fee_asset_item: u32,
weight: Weight,
) -> EvmResult {
// TODO: account for a possible storage read inside LocationMatcher::convert()

let origin = Runtime::AddressMapping::into_account_id(handle.context().caller);
let assets: Vec<_> = assets.into();

let assets_to_send: Vec<Asset> = 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);

let call = pallet_xcm::Call::<Runtime>::transfer_assets {
dest: Box::new(VersionedLocation::V4(dest)),
beneficiary: Box::new(VersionedLocation::V4(beneficiary)),
assets: Box::new(VersionedAssets::V4(assets_to_send.into())),
fee_asset_item,
weight_limit,
};

RuntimeHelper::<Runtime>::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<U256, u128>)>,
) -> Result<Vec<Asset>, PrecompileFailure> {
let mut assets_to_send: Vec<Asset> = vec![];
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 {
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)
}
}
2 changes: 1 addition & 1 deletion precompiles/pallet-xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ use xcm_executor::{
traits::{ConvertLocation, TransactAsset, WeightTrader},
AssetsInHolding,
};
pub use xcm_primitives::location_converter::{
pub use xcm_primitives::location_matcher::{
AssetIdInfoGetter, Erc20PalletMatcher, MatchThroughEquivalence, SingleAddressMatcher,
};
use Junctions::Here;
Expand Down
13 changes: 8 additions & 5 deletions precompiles/pallet-xcm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,10 @@ fn test_solidity_interface_has_all_function_selectors_documented_and_implemented

#[test]
fn selectors() {
assert!(PCall::transfer_assets_selectors().contains(&0x650ef8c7));
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));
}

#[test]
Expand All @@ -39,7 +42,7 @@ fn modifiers() {
let mut tester =
PrecompilesModifierTester::new(PrecompilesValue::get(), Alice, Precompile1);

tester.test_default_modifier(PCall::transfer_assets_selectors());
tester.test_default_modifier(PCall::transfer_assets_location_selectors());
});
}

Expand Down Expand Up @@ -86,7 +89,7 @@ fn test_transfer_assets_works() {
.prepare_test(
Alice,
Precompile1,
PCall::transfer_assets {
PCall::transfer_assets_location {
dest,
beneficiary,
assets: vec![
Expand Down Expand Up @@ -130,7 +133,7 @@ fn test_transfer_assets_success_when_paying_fees_with_foreign_asset() {
.prepare_test(
Alice,
Precompile1,
PCall::transfer_assets {
PCall::transfer_assets_location {
dest,
beneficiary,
assets: vec![
Expand Down Expand Up @@ -176,7 +179,7 @@ fn test_transfer_assets_fails_fees_unknown_reserve() {
.prepare_test(
Alice,
Precompile1,
PCall::transfer_assets {
PCall::transfer_assets_location {
dest,
beneficiary,
assets: vec![
Expand Down
40 changes: 40 additions & 0 deletions primitives/xcm/src/generators.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// Copyright Moonsong Labs
// This file is part of Moonkit.

// Moonkit is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// Moonkit is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with Moonkit. If not, see <http://www.gnu.org/licenses/>.

use xcm::latest::{Junction::*, Location};

// TODO: Does it make sense to have this generators
// instead of retrieving the proper location on each case?
pub struct XcmSiblingDestinationGenerator;
impl XcmSiblingDestinationGenerator {
pub fn generate(para_id: u32) -> Location {
Location::new(1, Parachain(para_id))
}
}

pub struct XcmLocalBeneficiary20Generator;
impl XcmLocalBeneficiary20Generator {
pub fn generate(key: [u8; 20]) -> Location {
Location::new(0, AccountKey20 { network: None, key })
}
}

pub struct XcmLocalBeneficiary32Generator;
impl XcmLocalBeneficiary32Generator {
pub fn generate(id: [u8; 32]) -> Location {
Location::new(0, AccountId32 { network: None, id })
}
}
3 changes: 2 additions & 1 deletion primitives/xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@

use sp_runtime::DispatchResult;

pub mod location_converter;
pub mod generators;
pub mod location_matcher;

/// Pause and resume execution of XCM
pub trait PauseXcmExecution {
Expand Down
File renamed without changes.

0 comments on commit c048e7c

Please sign in to comment.