Skip to content

Commit

Permalink
refactor ForeignAssetMatcher
Browse files Browse the repository at this point in the history
  • Loading branch information
Agusrodri committed Apr 17, 2024
1 parent c048e7c commit f050e1f
Show file tree
Hide file tree
Showing 8 changed files with 67 additions and 102 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions precompiles/assets-erc20/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ sp-std = { workspace = true }
fp-evm = { workspace = true }
pallet-evm = { workspace = true, features = [ "forbid-evm-reentrancy" ] }

# Moonkit
xcm-primitives = { workspace = true }

[dev-dependencies]
hex-literal = { workspace = true }
libsecp256k1 = { workspace = true }
Expand Down Expand Up @@ -57,4 +60,5 @@ std = [
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"xcm-primitives/std",
]
11 changes: 1 addition & 10 deletions precompiles/assets-erc20/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use sp_std::{
convert::{TryFrom, TryInto},
marker::PhantomData,
};
use xcm_primitives::AccountIdAssetIdConversion;

mod eip2612;
use eip2612::Eip2612;
Expand All @@ -59,16 +60,6 @@ pub type BalanceOf<Runtime, Instance = ()> = <Runtime as pallet_assets::Config<I
/// Alias for the Asset Id type for the provided Runtime and Instance.
pub type AssetIdOf<Runtime, Instance = ()> = <Runtime as pallet_assets::Config<Instance>>::AssetId;

/// This trait ensure we can convert AccountIds to AssetIds
/// We will require Runtime to have this trait implemented
pub trait AccountIdAssetIdConversion<Account, AssetId> {
// Get assetId and prefix from account
fn account_to_asset_id(account: Account) -> Option<(Vec<u8>, AssetId)>;

// Get AccountId from AssetId and prefix
fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> Account;
}

/// The following distribution has been decided for the precompiles
/// 0-1023: Ethereum Mainnet Precompiles
/// 1024-2047 Precompiles that are not in Ethereum Mainnet but are neither Moonbeam specific
Expand Down
1 change: 1 addition & 0 deletions precompiles/assets-erc20/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ use sp_runtime::{
traits::{BlakeTwo256, ConstU32, IdentityLookup},
BuildStorage,
};
use xcm_primitives::AccountIdAssetIdConversion;

pub type AccountId = MockAccount;
pub type AssetId = u128;
Expand Down
45 changes: 37 additions & 8 deletions precompiles/pallet-xcm/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@ use xcm_executor::{
traits::{ConvertLocation, TransactAsset, WeightTrader},
AssetsInHolding,
};
pub use xcm_primitives::location_matcher::{
AssetIdInfoGetter, Erc20PalletMatcher, MatchThroughEquivalence, SingleAddressMatcher,
};
pub use xcm_primitives::{AccountIdAssetIdConversion, location_matcher::{
Erc20PalletMatcher, ForeignAssetMatcher, SingleAddressMatcher,
}};
use Junctions::Here;

pub type AccountId = MockAccount;
Expand Down Expand Up @@ -128,6 +128,35 @@ parameter_types! {
pub const MetadataDepositPerByte: u64 = 0;
}

pub const FOREIGN_ASSET_ADDRESS_PREFIX: &[u8] = &[255u8; 18];

// Instruct how to go from an H160 to an AssetID
// We just take the lowest 2 bytes
impl AccountIdAssetIdConversion<AccountId, AssetId> for Runtime {
/// The way to convert an account to assetId is by ensuring that the prefix is [0xFF, 18]
/// and by taking the lowest 2 bytes as the assetId
fn account_to_asset_id(account: AccountId) -> Option<(Vec<u8>, AssetId)> {
let h160_account: H160 = account.into();
let mut data = [0u8; 2];
let (prefix_part, id_part) = h160_account.as_fixed_bytes().split_at(18);
if prefix_part == FOREIGN_ASSET_ADDRESS_PREFIX {
data.copy_from_slice(id_part);
let asset_id: AssetId = u16::from_be_bytes(data);
Some((prefix_part.to_vec(), asset_id))
} else {
None
}
}

// The opposite conversion
fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> AccountId {
let mut data = [0u8; 20];
data[0..18].copy_from_slice(prefix);
data[18..20].copy_from_slice(&asset_id.to_be_bytes());
AccountId::from(data)
}
}

pub type AssetId = u16;

impl pallet_assets::Config for Runtime {
Expand Down Expand Up @@ -173,13 +202,13 @@ pub type AccountIdAlias = <mock::Runtime as frame_system::Config>::AccountId;

pub type SingleAddressMatch = SingleAddressMatcher<AccountIdAlias, 2050, Balances>;

pub type EquivalenceMatch =
MatchThroughEquivalence<AccountIdAlias, AssetId, AssetIdInfoGetter, ForeignAssetCreator>;
pub type ForeignAssetMatch =
ForeignAssetMatcher<AccountIdAlias, AssetId, mock::Runtime, ForeignAssetCreator>;

pub type Erc20Match = Erc20PalletMatcher<AccountIdAlias, 42>;

pub type PCall =
PalletXcmPrecompileCall<Runtime, (SingleAddressMatch, EquivalenceMatch, Erc20Match)>;
PalletXcmPrecompileCall<Runtime, (SingleAddressMatch, ForeignAssetMatch, Erc20Match)>;

mock_account!(ParentAccount, |_| MockAccount::from_u64(4));

Expand All @@ -195,7 +224,7 @@ const BLOCK_STORAGE_LIMIT: u64 = 40 * 1024;

parameter_types! {
pub BlockGasLimit: U256 = U256::from(u64::MAX);
pub PrecompilesValue: Precompiles<Runtime, (SingleAddressMatch, EquivalenceMatch, Erc20Match)> = Precompiles::new();
pub PrecompilesValue: Precompiles<Runtime, (SingleAddressMatch, ForeignAssetMatch, Erc20Match)> = Precompiles::new();
pub const WeightPerGas: Weight = Weight::from_parts(1, 0);
pub GasLimitPovSizeRatio: u64 = {
let block_gas_limit = BlockGasLimit::get().min(u64::MAX.into()).low_u64();
Expand Down Expand Up @@ -230,7 +259,7 @@ impl pallet_evm::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type Runner = pallet_evm::runner::stack::Runner<Self>;
type PrecompilesValue = PrecompilesValue;
type PrecompilesType = Precompiles<Self, (SingleAddressMatch, EquivalenceMatch, Erc20Match)>;
type PrecompilesType = Precompiles<Self, (SingleAddressMatch, ForeignAssetMatch, Erc20Match)>;
type ChainId = ();
type OnChargeTransaction = ();
type BlockGasLimit = BlockGasLimit;
Expand Down
2 changes: 1 addition & 1 deletion precompiles/pallet-xcm/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use precompile_utils::testing::*;
use sp_weights::Weight;
use xcm::latest::Junction::*;

fn precompiles() -> Precompiles<Runtime, (SingleAddressMatch, EquivalenceMatch, Erc20Match)> {
fn precompiles() -> Precompiles<Runtime, (SingleAddressMatch, ForeignAssetMatch, Erc20Match)> {
PrecompilesValue::get()
}

Expand Down
9 changes: 9 additions & 0 deletions primitives/xcm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,12 @@ impl PauseXcmExecution for () {
Ok(())
}
}

/// This trait ensure we can convert AccountIds to AssetIds.
pub trait AccountIdAssetIdConversion<Account, AssetId> {
// Get assetId and prefix from account
fn account_to_asset_id(account: Account) -> Option<(Vec<u8>, AssetId)>;

// Get AccountId from AssetId and prefix
fn asset_id_to_account(prefix: &[u8], asset_id: AssetId) -> Account;
}
96 changes: 13 additions & 83 deletions primitives/xcm/src/location_matcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,49 +14,13 @@
// You should have received a copy of the GNU General Public License
// along with Moonkit. If not, see <http://www.gnu.org/licenses/>.

use crate::AccountIdAssetIdConversion;
use frame_support::{traits::PalletInfoAccess, Parameter};
use sp_core::H160;
use sp_runtime::traits::MaybeEquivalence;
use sp_std::{fmt::Debug, marker::PhantomData, mem::size_of};
use sp_std::{fmt::Debug, marker::PhantomData};
use xcm::latest::{Junction::*, Location};

/// Information to retrieve for a specific AssetId type.
pub struct AssetIdInfo<'a> {
pub foreign_asset_prefix: &'a [u8],
pub size_of: usize,
}

// Define a trait to abstract over different types of AssetId.
// We retrieve an AssetIdInfo type containing all the information we need
// to handle the generic types over AssetId.
pub trait GetAssetIdInfo<T> {
fn get_asset_id_info() -> AssetIdInfo<'static>;
}

/// A getter that contains the info for each type
/// we admit as AssetId.
pub struct AssetIdInfoGetter;

// Implement GetAssetId trait for u128.
impl GetAssetIdInfo<u128> for AssetIdInfoGetter {
fn get_asset_id_info() -> AssetIdInfo<'static> {
AssetIdInfo {
foreign_asset_prefix: &[255u8; 4],
size_of: size_of::<u128>(),
}
}
}

// Implement GetAssetId trait for u16.
impl GetAssetIdInfo<u16> for AssetIdInfoGetter {
fn get_asset_id_info() -> AssetIdInfo<'static> {
AssetIdInfo {
foreign_asset_prefix: &[255u8; 18],
size_of: size_of::<u16>(),
}
}
}

/// A converter from AccountId to a XCM Location.
pub trait AccountIdToLocationMatcher<AccountId> {
fn convert(account: AccountId) -> Option<Location>;
Expand Down Expand Up @@ -99,63 +63,29 @@ where
}

/// Matcher to compare a received account against some possible foreign asset address.
pub struct MatchThroughEquivalence<AccountId, AssetId, AssetIdInfoGetter, AssetIdToLocationManager>(
pub struct ForeignAssetMatcher<
AccountId,
AssetId,
AccountIdAssetIdConverter,
AssetIdToLocationManager,
>(
PhantomData<(
AccountId,
AssetId,
AssetIdInfoGetter,
AccountIdAssetIdConverter,
AssetIdToLocationManager,
)>,
);

impl<AccountId, AssetId, AssetIdInfoGetter, AssetIdToLocationManager>
MatchThroughEquivalence<AccountId, AssetId, AssetIdInfoGetter, AssetIdToLocationManager>
where
AccountId: Parameter + Into<H160>,
AssetId: From<u8> + TryFrom<u16> + TryFrom<u128>,
{
// Helper function that retrieves the asset_id of a foreign asset account.
pub fn account_to_asset_id(
account: AccountId,
asset_id_info: AssetIdInfo,
) -> Option<(Vec<u8>, AssetId)> {
let h160_account: H160 = account.into();
let (prefix_part, id_part) = h160_account
.as_fixed_bytes()
.split_at(asset_id_info.size_of + 2);

if prefix_part == asset_id_info.foreign_asset_prefix {
let asset_id: AssetId = match asset_id_info.size_of {
2 => {
let mut data = [0u8; 2];
data.copy_from_slice(id_part);
u16::from_be_bytes(data).try_into().unwrap_or(0u8.into())
}
16 => {
let mut data = [0u8; 16];
data.copy_from_slice(id_part);
u128::from_be_bytes(data).try_into().unwrap_or(0u8.into())
}
_ => return None,
};
return Some((prefix_part.to_vec(), asset_id));
}
None
}
}

impl<AccountId, AssetId, AssetIdInfoGetter, AssetIdToLocationManager>
impl<AccountId, AssetId, AccountIdAssetIdConverter, AssetIdToLocationManager>
AccountIdToLocationMatcher<AccountId>
for MatchThroughEquivalence<AccountId, AssetId, AssetIdInfoGetter, AssetIdToLocationManager>
for ForeignAssetMatcher<AccountId, AssetId, AccountIdAssetIdConverter, AssetIdToLocationManager>
where
AccountId: Parameter + Into<H160>,
AssetId: From<u8> + TryFrom<u16> + TryFrom<u128>,
AssetIdInfoGetter: GetAssetIdInfo<AssetId>,
AccountIdAssetIdConverter: AccountIdAssetIdConversion<AccountId, AssetId>,
AssetIdToLocationManager: MaybeEquivalence<Location, AssetId>,
{
fn convert(account: AccountId) -> Option<Location> {
let asset_id_info = AssetIdInfoGetter::get_asset_id_info();
if let Some((_prefix, asset_id)) = Self::account_to_asset_id(account, asset_id_info) {
if let Some((_prefix, asset_id)) = AccountIdAssetIdConverter::account_to_asset_id(account) {
return AssetIdToLocationManager::convert_back(&asset_id);
}
None
Expand Down

0 comments on commit f050e1f

Please sign in to comment.