referrerpolicy=no-referrer-when-downgrade

pallet_assets_precompiles/
foreign_assets.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: Apache-2.0
5
6// Licensed under the Apache License, Version 2.0 (the "License");
7// you may not use this file except in compliance with the License.
8// You may obtain a copy of the License at
9//
10// 	http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18use core::marker::PhantomData;
19use pallet_assets::AssetsCallback;
20
21pub use pallet::*;
22
23const LOG_TARGET: &str = "pallet_foreign_assets";
24
25pub struct ForeignAssetId<T, I = ()>(PhantomData<(T, I)>);
26impl<T: Config, I> AssetsCallback<T::AssetId, T::AccountId> for ForeignAssetId<T, I>
27where
28	T: Config<ForeignAssetId = T::AssetId> + pallet_assets::Config<I>,
29	I: 'static,
30{
31	fn created(id: &T::AssetId, _: &T::AccountId) -> Result<(), ()> {
32		Pallet::<T>::insert_asset_mapping(id).map(|_| ())
33	}
34
35	fn destroyed(id: &T::AssetId) -> Result<(), ()> {
36		Pallet::<T>::remove_asset_mapping(id);
37		Ok(())
38	}
39}
40
41#[frame_support::pallet]
42pub mod pallet {
43	use super::*;
44	use frame_support::{pallet_prelude::*, Blake2_128Concat};
45
46	#[pallet::config]
47	pub trait Config: frame_system::Config {
48		/// The foreign asset ID type. This must match the `AssetId` type used by the
49		/// `pallet_assets` instance for foreign assets.
50		type ForeignAssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
51
52		/// The `pallet_assets` instance that holds foreign assets.
53		/// Used by benchmarks to interact with the correct assets instance.
54		#[cfg(feature = "runtime-benchmarks")]
55		type AssetsInstance: 'static;
56	}
57
58	const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
59
60	#[pallet::pallet]
61	#[pallet::storage_version(STORAGE_VERSION)]
62	pub struct Pallet<T>(_);
63
64	/// The next available asset index for foreign assets.
65	/// This is incremented each time a new foreign asset mapping is created.
66	#[pallet::storage]
67	pub type NextAssetIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
68
69	/// Mapping an asset index (derived from the precompile address) to a `ForeignAssetId`.
70	#[pallet::storage]
71	pub type AssetIndexToForeignAssetId<T: Config> =
72		StorageMap<_, Identity, u32, T::ForeignAssetId, OptionQuery>;
73
74	/// Mapping a `ForeignAssetId` to an asset index (used for deriving precompile addresses).
75	#[pallet::storage]
76	pub type ForeignAssetIdToAssetIndex<T: Config> =
77		StorageMap<_, Blake2_128Concat, T::ForeignAssetId, u32, OptionQuery>;
78
79	impl<T: Config> Pallet<T> {
80		/// Get the foreign asset ID for a given asset index.
81		pub fn asset_id_of(asset_index: u32) -> Option<T::ForeignAssetId> {
82			AssetIndexToForeignAssetId::<T>::get(asset_index)
83		}
84
85		/// Get the asset index for a given foreign asset ID.
86		pub fn asset_index_of(asset_id: &T::ForeignAssetId) -> Option<u32> {
87			ForeignAssetIdToAssetIndex::<T>::get(asset_id)
88		}
89
90		/// Get the next available asset index without incrementing it.
91		pub fn next_asset_index() -> u32 {
92			NextAssetIndex::<T>::get()
93		}
94
95		/// Insert a new asset mapping, allocating a sequential index.
96		/// Returns the allocated asset index on success.
97		pub fn insert_asset_mapping(asset_id: &T::ForeignAssetId) -> Result<u32, ()> {
98			if ForeignAssetIdToAssetIndex::<T>::contains_key(asset_id) {
99				log::error!(target: LOG_TARGET, "Asset id {:?} already mapped", asset_id);
100				return Err(());
101			}
102
103			let asset_index = NextAssetIndex::<T>::get();
104			let next_index = asset_index.checked_add(1).ok_or_else(|| {
105				log::error!(target: LOG_TARGET, "Asset index overflow");
106			})?;
107
108			AssetIndexToForeignAssetId::<T>::insert(asset_index, asset_id.clone());
109			ForeignAssetIdToAssetIndex::<T>::insert(asset_id, asset_index);
110			NextAssetIndex::<T>::put(next_index);
111
112			log::debug!(target: LOG_TARGET, "Mapped asset {:?} to index {:?}", asset_id, asset_index);
113			Ok(asset_index)
114		}
115
116		/// Remove an asset mapping if it exists, else this function has no effect.
117		pub fn remove_asset_mapping(asset_id: &T::ForeignAssetId) {
118			if let Some(asset_index) = ForeignAssetIdToAssetIndex::<T>::take(asset_id) {
119				AssetIndexToForeignAssetId::<T>::remove(asset_index);
120			}
121		}
122	}
123}