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>
27	AssetsCallback<T::AssetId, T::AccountId, <T as pallet_assets::Config<I>>::Balance>
28	for ForeignAssetId<T, I>
29where
30	T: Config<ForeignAssetId = T::AssetId> + pallet_assets::Config<I>,
31	I: 'static,
32{
33	fn created(id: &T::AssetId, _: &T::AccountId) -> Result<(), ()> {
34		Pallet::<T>::insert_asset_mapping(id).map(|_| ())
35	}
36
37	fn destroyed(id: &T::AssetId) -> Result<(), ()> {
38		Pallet::<T>::remove_asset_mapping(id);
39		Ok(())
40	}
41}
42
43#[frame_support::pallet]
44pub mod pallet {
45	use super::*;
46	use frame_support::{pallet_prelude::*, Blake2_128Concat};
47
48	#[pallet::config]
49	pub trait Config: frame_system::Config {
50		/// The foreign asset ID type. This must match the `AssetId` type used by the
51		/// `pallet_assets` instance for foreign assets.
52		type ForeignAssetId: Member + Parameter + Clone + MaybeSerializeDeserialize + MaxEncodedLen;
53
54		/// The `pallet_assets` instance that holds foreign assets.
55		/// Used by benchmarks to interact with the correct assets instance.
56		#[cfg(feature = "runtime-benchmarks")]
57		type AssetsInstance: 'static;
58	}
59
60	const STORAGE_VERSION: StorageVersion = StorageVersion::new(0);
61
62	#[pallet::pallet]
63	#[pallet::storage_version(STORAGE_VERSION)]
64	pub struct Pallet<T>(_);
65
66	/// The next available asset index for foreign assets.
67	/// This is incremented each time a new foreign asset mapping is created.
68	#[pallet::storage]
69	pub type NextAssetIndex<T: Config> = StorageValue<_, u32, ValueQuery>;
70
71	/// Mapping an asset index (derived from the precompile address) to a `ForeignAssetId`.
72	#[pallet::storage]
73	pub type AssetIndexToForeignAssetId<T: Config> =
74		StorageMap<_, Identity, u32, T::ForeignAssetId, OptionQuery>;
75
76	/// Mapping a `ForeignAssetId` to an asset index (used for deriving precompile addresses).
77	#[pallet::storage]
78	pub type ForeignAssetIdToAssetIndex<T: Config> =
79		StorageMap<_, Blake2_128Concat, T::ForeignAssetId, u32, OptionQuery>;
80
81	impl<T: Config> Pallet<T> {
82		/// Get the foreign asset ID for a given asset index.
83		pub fn asset_id_of(asset_index: u32) -> Option<T::ForeignAssetId> {
84			AssetIndexToForeignAssetId::<T>::get(asset_index)
85		}
86
87		/// Get the asset index for a given foreign asset ID.
88		pub fn asset_index_of(asset_id: &T::ForeignAssetId) -> Option<u32> {
89			ForeignAssetIdToAssetIndex::<T>::get(asset_id)
90		}
91
92		/// Get the next available asset index without incrementing it.
93		pub fn next_asset_index() -> u32 {
94			NextAssetIndex::<T>::get()
95		}
96
97		/// Insert a new asset mapping, allocating a sequential index.
98		/// Returns the allocated asset index on success.
99		pub fn insert_asset_mapping(asset_id: &T::ForeignAssetId) -> Result<u32, ()> {
100			if ForeignAssetIdToAssetIndex::<T>::contains_key(asset_id) {
101				log::error!(target: LOG_TARGET, "Asset id {:?} already mapped", asset_id);
102				return Err(());
103			}
104
105			let asset_index = NextAssetIndex::<T>::get();
106			let next_index = asset_index.checked_add(1).ok_or_else(|| {
107				log::error!(target: LOG_TARGET, "Asset index overflow");
108			})?;
109
110			AssetIndexToForeignAssetId::<T>::insert(asset_index, asset_id.clone());
111			ForeignAssetIdToAssetIndex::<T>::insert(asset_id, asset_index);
112			NextAssetIndex::<T>::put(next_index);
113
114			log::debug!(target: LOG_TARGET, "Mapped asset {:?} to index {:?}", asset_id, asset_index);
115			Ok(asset_index)
116		}
117
118		/// Remove an asset mapping if it exists, else this function has no effect.
119		pub fn remove_asset_mapping(asset_id: &T::ForeignAssetId) {
120			if let Some(asset_index) = ForeignAssetIdToAssetIndex::<T>::take(asset_id) {
121				AssetIndexToForeignAssetId::<T>::remove(asset_index);
122			}
123		}
124	}
125}