Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
dc1b49d
added evm-compat crate in pallet-assets
0xRVE Jan 21, 2026
01bc26f
moved logic into assets/precompiles
0xRVE Jan 22, 2026
4881b06
Revert "added evm-compat crate in pallet-assets"
0xRVE Jan 22, 2026
e1aed98
Update from github-actions[bot] running command 'prdoc --audience run…
github-actions[bot] Jan 22, 2026
518483d
Update pr_10869.prdoc
0xRVE Jan 22, 2026
6d83853
zepter
0xRVE Jan 22, 2026
4f0d2da
Merge branch 'rve/8659-assets-precompile' of github.com-work:parityte…
0xRVE Jan 22, 2026
03c1bd4
added check to avoid foreign asset hash collision
0xRVE Jan 22, 2026
1b3ba4f
update prdoc
0xRVE Jan 22, 2026
9623664
fix compilation
0xRVE Jan 22, 2026
8f9530c
clippy
0xRVE Jan 22, 2026
06b9f65
zepter
0xRVE Jan 23, 2026
99a6617
added tests
0xRVE Jan 23, 2026
bf81f0b
taplo
0xRVE Jan 23, 2026
3a828f1
fix ci
0xRVE Jan 23, 2026
aeb3994
fix ci
0xRVE Jan 23, 2026
c5c92d7
fix error
0xRVE Jan 23, 2026
20b06a0
fixreview comments
0xRVE Jan 26, 2026
c90d1f9
fix test
0xRVE Jan 26, 2026
8137af0
added migration
0xRVE Jan 26, 2026
749909b
Merge remote-tracking branch 'origin/master' into rve/8659-assets-pre…
0xRVE Jan 26, 2026
5acbcd0
added migration to runtime
0xRVE Jan 26, 2026
407c3ab
xonvert migration to SteppedMigration
0xRVE Jan 26, 2026
7ac170f
format
0xRVE Jan 26, 2026
d3f3ee1
fix test
0xRVE Jan 26, 2026
50a8fe2
fix tests
0xRVE Jan 26, 2026
166086b
fix comments
0xRVE Jan 26, 2026
32ac7af
removed unused impl
0xRVE Jan 27, 2026
5f78f44
moved migration to pallet-assets-precompiles
0xRVE Jan 29, 2026
d557688
changed foreign asset mapping from hash to sequential index
0xRVE Jan 29, 2026
9fb29b6
format
0xRVE Jan 29, 2026
6a7df21
added benchmarking for migration
0xRVE Jan 29, 2026
85d07e5
cleanup
0xRVE Jan 29, 2026
7e5646e
format
0xRVE Jan 29, 2026
4558a0c
cargo clippy
0xRVE Jan 29, 2026
b512057
format
0xRVE Jan 29, 2026
4093bf1
added pallet-assets-precompiles to node runtime
0xRVE Jan 30, 2026
3166112
Update from github-actions[bot] running command 'bench --runtime dev …
github-actions[bot] Jan 30, 2026
a5469d7
review fixes
0xRVE Jan 30, 2026
c9175e7
fix migration.rs
0xRVE Jan 30, 2026
98d5544
in migration.rs change can_consume+consume to try_consume
0xRVE Jan 30, 2026
8f59004
removed some code and comments in migration_benchmark.rs
0xRVE Jan 30, 2026
31b04ed
in tests.rs review fixes
0xRVE Jan 30, 2026
2b7b273
format
0xRVE Jan 30, 2026
bc74d06
removed idempotent
0xRVE Jan 30, 2026
a4e3390
removed redundant pallet::
0xRVE Jan 30, 2026
26cad5c
changed Blake2_128Concat to IDentity
0xRVE Jan 30, 2026
6971006
remove unused import
0xRVE Jan 30, 2026
a167412
make migration benchmark pub(crate)
0xRVE Jan 30, 2026
906d510
remove redundant exports
0xRVE Jan 30, 2026
a4df7bc
added Blake2_128Concat for AssetId(Location) -> u32 map
0xRVE Jan 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

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

20 changes: 16 additions & 4 deletions cumulus/parachains/runtimes/assets/asset-hub-westend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ use frame_system::{
EnsureRoot, EnsureSigned, EnsureSignedBy,
};
use pallet_asset_conversion_tx_payment::SwapAssetAdapter;
use pallet_assets_precompiles::{InlineIdConfig, ERC20};
use pallet_assets_precompiles::{ForeignAssetId, ForeignIdConfig, InlineIdConfig, ERC20};
use pallet_nfts::{DestroyWitness, PalletFeatures};
use pallet_nomination_pools::PoolId;
use pallet_revive::evm::runtime::EthExtra;
Expand Down Expand Up @@ -570,6 +570,11 @@ parameter_types! {
pub const ForeignAssetsMetadataDepositPerByte: Balance = MetadataDepositPerByte::get();
}

impl pallet_assets_precompiles::ForeignAssetsConfig for Runtime {
// must match the AssetId type used by the `ForeignAssets` instance
type ForeignAssetId = <Runtime as pallet_assets::Config<ForeignAssetsInstance>>::AssetId;
}

/// Assets managed by some foreign location. Note: we do not declare a `ForeignAssetsCall` type, as
/// this type is used in proxy definitions. We assume that a foreign location would not want to set
/// an individual, local account as a proxy for the issuance of their assets. This issuance should
Expand Down Expand Up @@ -602,7 +607,7 @@ impl pallet_assets::Config<ForeignAssetsInstance> for Runtime {
type Freezer = ForeignAssetsFreezer;
type Extra = ();
type WeightInfo = weights::pallet_assets_foreign::WeightInfo<Runtime>;
type CallbackHandle = ();
type CallbackHandle = (ForeignAssetId<Runtime, ForeignAssetsInstance>,);
type AssetAccountDeposit = ForeignAssetsAssetAccountDeposit;
type RemoveItemsLimit = frame_support::traits::ConstU32<1000>;
#[cfg(feature = "runtime-benchmarks")]
Expand Down Expand Up @@ -1199,6 +1204,7 @@ impl pallet_revive::Config for Runtime {
type Precompiles = (
ERC20<Self, InlineIdConfig<0x120>, TrustBackedAssetsInstance>,
ERC20<Self, InlineIdConfig<0x320>, PoolAssetsInstance>,
ERC20<Self, ForeignIdConfig<0x220, Self, ForeignAssetsInstance>, ForeignAssetsInstance>,
XcmPrecompile<Self>,
);
type AddressMapper = pallet_revive::AccountId32Mapper<Self>;
Expand Down Expand Up @@ -1226,12 +1232,17 @@ parameter_types! {
impl pallet_migrations::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
#[cfg(not(feature = "runtime-benchmarks"))]
type Migrations =
type Migrations = (
assets_common::migrations::foreign_assets_reserves::ForeignAssetsReservesMigration<
Runtime,
ForeignAssetsInstance,
migrations::AssetHubWestendForeignAssetsReservesProvider,
>;
>,
pallet_assets_precompiles::MigrateForeignAssetPrecompileMappings<
Runtime,
ForeignAssetsInstance,
>,
);
// Benchmarks need mocked migrations to guarantee that they succeed.
#[cfg(feature = "runtime-benchmarks")]
type Migrations = pallet_migrations::mock_helpers::MockedMigrations;
Expand Down Expand Up @@ -1373,6 +1384,7 @@ construct_runtime!(
Revive: pallet_revive = 60,

AssetRewards: pallet_asset_rewards = 61,
AssetsPrecompiles: pallet_assets_precompiles::pallet = 62,

StateTrieMigration: pallet_state_trie_migration = 70,

Expand Down
23 changes: 23 additions & 0 deletions prdoc/pr_10869.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
title: '[pallet-assets] add ForeignAssetIdExtractor to assets precompile'
doc:
- audience: Runtime Dev
description: |-
Fixes https://github.com/paritytech/polkadot-sdk/issues/8659

Implements `ForeignAssetIdExtractor` trait for pallet-assets-precompiles that enables conversion of u32 asset IDs to XCM Location types. This allows smart contracts to interact with foreign assets through a standardized interface.

Key additions:
- `ForeignAssetIdExtractor` implementation for converting asset indices to foreign asset IDs
- Indexing mechanism via `AssetIndexToForeignAssetId` storage to map u32 indices to location-based asset IDs
- created new pallet for holding the StorageMaps: pallet-assets-precompiles.
- Validation logic to prevent hash collisions during asset index registration
- Integration with asset-hub-westend runtime

crates:
- name: pallet-assets
bump: minor
- name: pallet-assets-precompiles
bump: minor
- name: asset-hub-westend-runtime
bump: minor

8 changes: 8 additions & 0 deletions substrate/bin/node/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1459,6 +1459,10 @@ impl pallet_multi_asset_bounties::Config for Runtime {
type BenchmarkHelper = PalletMultiAssetBountiesArguments;
}

impl pallet_assets_precompiles::ForeignAssetsConfig for Runtime {
type ForeignAssetId = u32;
}

impl pallet_tips::Config for Runtime {
type RuntimeEvent = RuntimeEvent;
type DataDepositPerByte = DataDepositPerByte;
Expand Down Expand Up @@ -2870,6 +2874,9 @@ mod runtime {

#[runtime::pallet_index(90)]
pub type MultiAssetBounties = pallet_multi_asset_bounties::Pallet<Runtime>;

#[runtime::pallet_index(91)]
pub type AssetsPrecompiles = pallet_assets_precompiles::pallet::Pallet<Runtime>;
}

/// The address format for describing accounts.
Expand Down Expand Up @@ -3147,6 +3154,7 @@ mod benches {
[pallet_migrations, MultiBlockMigrations]
[pallet_mmr, Mmr]
[pallet_multi_asset_bounties, MultiAssetBounties]
[pallet_assets_precompiles, AssetsPrecompiles]
[pallet_multisig, Multisig]
[pallet_nomination_pools, NominationPoolsBench::<Runtime>]
[pallet_offences, OffencesBench::<Runtime>]
Expand Down
19 changes: 14 additions & 5 deletions substrate/frame/assets/precompiles/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,41 +15,50 @@ workspace = true
targets = ["x86_64-unknown-linux-gnu"]

[dependencies]
codec = { workspace = true }
ethereum-standards = { workspace = true }
frame-benchmarking = { workspace = true, optional = true }
frame-support = { workspace = true }
frame-system = { workspace = true }
log = { workspace = true }
pallet-assets = { workspace = true }
pallet-revive = { workspace = true }
scale-info = { workspace = true }
sp-core = { workspace = true }
sp-runtime = { workspace = true }
xcm = { workspace = true }

[dev-dependencies]
codec = { workspace = true }
frame-system = { workspace = true }
pallet-balances = { workspace = true }
scale-info = { workspace = true }
sp-core = { workspace = true }
sp-io = { workspace = true }
sp-runtime = { workspace = true }
test-case = { workspace = true }

[features]
default = ["std"]
std = [
"codec/std",
"frame-benchmarking?/std",
"frame-support/std",
"frame-system/std",
"log/std",
"pallet-assets/std",
"pallet-balances/std",
"pallet-revive/std",
"scale-info/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"xcm/std",
]
runtime-benchmarks = [
"frame-benchmarking/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
"pallet-assets/runtime-benchmarks",
"pallet-balances/runtime-benchmarks",
"pallet-revive/runtime-benchmarks",
"sp-runtime/runtime-benchmarks",
"xcm/runtime-benchmarks",
]
try-runtime = [
"frame-support/try-runtime",
Expand Down
115 changes: 115 additions & 0 deletions substrate/frame/assets/precompiles/src/foreign_assets.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use core::marker::PhantomData;
use frame_support::LOG_TARGET;
use pallet_assets::AssetsCallback;

pub use pallet::*;

pub struct ForeignAssetId<T, I = ()>(PhantomData<(T, I)>);
impl<T: Config, I> AssetsCallback<T::AssetId, T::AccountId> for ForeignAssetId<T, I>
where
T: Config<ForeignAssetId = T::AssetId> + pallet_assets::Config<I>,
I: 'static,
{
fn created(id: &T::AssetId, _: &T::AccountId) -> Result<(), ()> {
Pallet::<T>::insert_asset_mapping(id).map(|_| ())
}

fn destroyed(id: &T::AssetId) -> Result<(), ()> {
Pallet::<T>::remove_asset_mapping(id);
Ok(())
}
}

#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::{pallet_prelude::*, Blake2_128Concat};

#[pallet::config]
pub trait Config: frame_system::Config {
/// The foreign asset ID type. This must match the `AssetId` type used by the
/// `pallet_assets` instance for foreign assets.
type ForeignAssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really think we should simplify where we can and not use an associated type here where we can hardcode it to Location, will test what it looks like without it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@franciscoaguirre what do you think ? or maybe it's annoying because we need to update the Location version everytime you bump it here

  580  impl pallet_assets::Config<ForeignAssetsInstance> for Runtime {
     1 ▎   type RuntimeEvent = RuntimeEvent;
     2 ▎   type Balance = Balance;
     3 ▎   type AssetId = xcm::v5::Location;

Copy link
Contributor

@pgherveou pgherveou Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tasked Claude with it here #10945
also @franciscoaguirre Location encoding is stable ? if AH use v6 in a couple of month would an existing Location encode to the same value (I assume yes)

}

#[pallet::pallet]
pub struct Pallet<T>(_);

/// The next available asset index for foreign assets.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we need anything pub in this pallet, all we need is the AssetsCallback and the ForeignAssetIdExtractor that I will keep in that module to keep everything private

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was able to remove a few pubs

/// This is incremented each time a new foreign asset mapping is created.
#[pallet::storage]
pub type NextAssetIndex<T: Config> = StorageValue<_, u32, ValueQuery>;

/// Mapping an asset index (derived from the precompile address) to a `ForeignAssetId`.
#[pallet::storage]
pub type AssetIndexToForeignAssetId<T: Config> =
StorageMap<_, Identity, u32, T::ForeignAssetId, OptionQuery>;

/// Mapping a `ForeignAssetId` to an asset index (used for deriving precompile addresses).
#[pallet::storage]
pub type ForeignAssetIdToAssetIndex<T: Config> =
StorageMap<_, Blake2_128Concat, T::ForeignAssetId, u32, OptionQuery>;

impl<T: Config> Pallet<T> {
/// Get the foreign asset ID for a given asset index.
pub fn asset_id_of(asset_index: u32) -> Option<T::ForeignAssetId> {
AssetIndexToForeignAssetId::<T>::get(asset_index)
}

/// Get the asset index for a given foreign asset ID.
pub fn asset_index_of(asset_id: &T::ForeignAssetId) -> Option<u32> {
ForeignAssetIdToAssetIndex::<T>::get(asset_id)
}

/// Get the next available asset index without incrementing it.
pub fn next_asset_index() -> u32 {
NextAssetIndex::<T>::get()
}

/// Insert a new asset mapping, allocating a sequential index.
/// Returns the allocated asset index on success.
pub fn insert_asset_mapping(asset_id: &T::ForeignAssetId) -> Result<u32, ()> {
if ForeignAssetIdToAssetIndex::<T>::contains_key(asset_id) {
log::error!(target: LOG_TARGET, "Asset id {:?} already mapped", asset_id);
return Err(());
}

let asset_index = NextAssetIndex::<T>::get();
let next_index = asset_index.checked_add(1).ok_or_else(|| {
log::error!(target: LOG_TARGET, "Asset index overflow");
()
})?;

AssetIndexToForeignAssetId::<T>::insert(asset_index, asset_id.clone());
ForeignAssetIdToAssetIndex::<T>::insert(asset_id, asset_index);
NextAssetIndex::<T>::put(next_index);

log::debug!(target: LOG_TARGET, "Mapped asset {:?} to index {:?}", asset_id, asset_index);
Ok(asset_index)
}

pub fn remove_asset_mapping(asset_id: &T::ForeignAssetId) {
if let Some(asset_index) = ForeignAssetIdToAssetIndex::<T>::get(&asset_id) {
AssetIndexToForeignAssetId::<T>::remove(asset_index);
ForeignAssetIdToAssetIndex::<T>::remove(asset_id);
}
}
}
}
Loading
Loading