12345678910111213141516171819202122232425262728293031323334#![cfg_attr(not(feature = "std"), no_std)]35#![allow(clippy::unused_unit)]3637use frame_support::{38 dispatch::DispatchResult,39 ensure,40 pallet_prelude::*,41 traits::{fungible, fungibles, Currency, EnsureOrigin},42};43use frame_system::pallet_prelude::*;44use pallet_common::erc::CrossAccountId;45use pallet_fungible::Pallet as PalletFungible;46use scale_info::TypeInfo;47use serde::{Deserialize, Serialize};48use sp_runtime::{49 traits::{One, Zero},50 ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use staging_xcm::{latest::MultiLocation, VersionedMultiLocation};545556use staging_xcm::{57 opaque::latest::{prelude::XcmError, Weight},58 v3::XcmContext,59};60use staging_xcm_executor::{traits::WeightTrader, Assets};61use up_data_structs::{CollectionId, CollectionMode, CreateCollectionData, TokenId};626364656667#[derive(68 Clone,69 Copy,70 Eq,71 PartialEq,72 PartialOrd,73 Ord,74 MaxEncodedLen,75 RuntimeDebug,76 Encode,77 Decode,78 TypeInfo,79 Serialize,80 Deserialize,81)]82pub enum NativeCurrency {83 Here = 0,84 Parent = 1,85}8687#[derive(88 Clone,89 Copy,90 Eq,91 PartialEq,92 PartialOrd,93 Ord,94 MaxEncodedLen,95 RuntimeDebug,96 Encode,97 Decode,98 TypeInfo,99 Serialize,100 Deserialize,101)]102pub enum AssetId {103 ForeignAssetId(ForeignAssetId),104 NativeAssetId(NativeCurrency),105}106107pub trait TryAsForeign<T, F> {108 fn try_as_foreign(asset: T) -> Option<F>;109}110111impl TryAsForeign<AssetId, ForeignAssetId> for AssetId {112 fn try_as_foreign(asset: AssetId) -> Option<ForeignAssetId> {113 match asset {114 Self::ForeignAssetId(id) => Some(id),115 _ => None,116 }117 }118}119120pub type ForeignAssetId = u32;121pub type CurrencyId = AssetId;122123mod impl_fungibles;124pub mod weights;125126#[cfg(feature = "runtime-benchmarks")]127mod benchmarking;128129pub use module::*;130pub use weights::WeightInfo;131132133pub type BalanceOf<T> =134 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;135136137pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {138 139 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;140 141 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;142 143 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;144}145146pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);147148impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>149 for XcmForeignAssetIdMapping<T>150{151 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {152 log::trace!(target: "fassets::asset_metadatas", "call");153 Pallet::<T>::asset_metadatas(AssetId::ForeignAssetId(foreign_asset_id))154 }155156 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {157 log::trace!(target: "fassets::get_multi_location", "call");158 Pallet::<T>::foreign_asset_locations(foreign_asset_id)159 }160161 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {162 log::trace!(target: "fassets::get_currency_id", "call");163 Pallet::<T>::location_to_currency_ids(multi_location).map(AssetId::ForeignAssetId)164 }165}166167#[frame_support::pallet]168pub mod module {169 use super::*;170171 #[pallet::config]172 pub trait Config:173 frame_system::Config174 + pallet_common::Config175 + pallet_fungible::Config176 + orml_tokens::Config177 + pallet_balances::Config178 {179 180 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;181182 183 type Currency: Currency<Self::AccountId>;184185 186 type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;187188 189 type WeightInfo: WeightInfo;190 }191192 pub type AssetName = BoundedVec<u8, ConstU32<32>>;193 pub type AssetSymbol = BoundedVec<u8, ConstU32<7>>;194195 #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen)]196 pub struct AssetMetadata<Balance> {197 pub name: AssetName,198 pub symbol: AssetSymbol,199 pub decimals: u8,200 pub minimal_balance: Balance,201 }202203 #[pallet::error]204 pub enum Error<T> {205 206 207 BadLocation,208 209 MultiLocationExisted,210 211 AssetIdNotExists,212 213 AssetIdExisted,214 }215216 #[pallet::event]217 #[pallet::generate_deposit(fn deposit_event)]218 pub enum Event<T: Config> {219 220 ForeignAssetRegistered {221 asset_id: ForeignAssetId,222 asset_address: MultiLocation,223 metadata: AssetMetadata<BalanceOf<T>>,224 },225 226 ForeignAssetUpdated {227 asset_id: ForeignAssetId,228 asset_address: MultiLocation,229 metadata: AssetMetadata<BalanceOf<T>>,230 },231 232 AssetRegistered {233 asset_id: AssetId,234 metadata: AssetMetadata<BalanceOf<T>>,235 },236 237 AssetUpdated {238 asset_id: AssetId,239 metadata: AssetMetadata<BalanceOf<T>>,240 },241 }242243 244 245 246 #[pallet::storage]247 #[pallet::getter(fn next_foreign_asset_id)]248 pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;249 250 251 252 #[pallet::storage]253 #[pallet::getter(fn foreign_asset_locations)]254 pub type ForeignAssetLocations<T: Config> =255 StorageMap<_, Twox64Concat, ForeignAssetId, staging_xcm::v3::MultiLocation, OptionQuery>;256257 258 259 260 #[pallet::storage]261 #[pallet::getter(fn location_to_currency_ids)]262 pub type LocationToCurrencyIds<T: Config> =263 StorageMap<_, Twox64Concat, staging_xcm::v3::MultiLocation, ForeignAssetId, OptionQuery>;264265 266 267 268 #[pallet::storage]269 #[pallet::getter(fn asset_metadatas)]270 pub type AssetMetadatas<T: Config> =271 StorageMap<_, Twox64Concat, AssetId, AssetMetadata<BalanceOf<T>>, OptionQuery>;272273 274 275 #[pallet::storage]276 #[pallet::getter(fn asset_binding)]277 pub type AssetBinding<T: Config> =278 StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;279280 #[pallet::pallet]281 pub struct Pallet<T>(_);282283 #[pallet::call]284 impl<T: Config> Pallet<T> {285 #[pallet::call_index(0)]286 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]287 pub fn register_foreign_asset(288 origin: OriginFor<T>,289 owner: T::AccountId,290 location: Box<VersionedMultiLocation>,291 metadata: Box<AssetMetadata<BalanceOf<T>>>,292 ) -> DispatchResult {293 T::RegisterOrigin::ensure_origin(origin.clone())?;294295 let location: MultiLocation = (*location)296 .try_into()297 .map_err(|()| Error::<T>::BadLocation)?;298299 let md = metadata.clone();300 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();301 let mut description: Vec<u16> = "Foreign assets collection for "302 .encode_utf16()303 .collect::<Vec<u16>>();304 description.append(&mut name.clone());305306 let data: CreateCollectionData<T::CrossAccountId> = CreateCollectionData {307 name: name.try_into().unwrap(),308 description: description.try_into().unwrap(),309 mode: CollectionMode::Fungible(md.decimals),310 ..Default::default()311 };312 let owner = T::CrossAccountId::from_sub(owner);313 let bounded_collection_id =314 <PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;315 let foreign_asset_id =316 Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;317318 Self::deposit_event(Event::<T>::ForeignAssetRegistered {319 asset_id: foreign_asset_id,320 asset_address: location,321 metadata: *metadata,322 });323 Ok(())324 }325326 #[pallet::call_index(1)]327 #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]328 pub fn update_foreign_asset(329 origin: OriginFor<T>,330 foreign_asset_id: ForeignAssetId,331 location: Box<VersionedMultiLocation>,332 metadata: Box<AssetMetadata<BalanceOf<T>>>,333 ) -> DispatchResult {334 T::RegisterOrigin::ensure_origin(origin)?;335336 let location: MultiLocation = (*location)337 .try_into()338 .map_err(|()| Error::<T>::BadLocation)?;339 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;340341 Self::deposit_event(Event::<T>::ForeignAssetUpdated {342 asset_id: foreign_asset_id,343 asset_address: location,344 metadata: *metadata,345 });346 Ok(())347 }348 }349}350351impl<T: Config> Pallet<T> {352 fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {353 NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {354 let id = *current;355 *current = current356 .checked_add(One::one())357 .ok_or(ArithmeticError::Overflow)?;358 Ok(id)359 })360 }361362 fn do_register_foreign_asset(363 location: &MultiLocation,364 metadata: &AssetMetadata<BalanceOf<T>>,365 bounded_collection_id: CollectionId,366 ) -> Result<ForeignAssetId, DispatchError> {367 let foreign_asset_id = Self::get_next_foreign_asset_id()?;368 LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {369 ensure!(370 maybe_currency_ids.is_none(),371 Error::<T>::MultiLocationExisted372 );373 *maybe_currency_ids = Some(foreign_asset_id);374 375376 ForeignAssetLocations::<T>::try_mutate(377 foreign_asset_id,378 |maybe_location| -> DispatchResult {379 ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);380 *maybe_location = Some(*location);381382 AssetMetadatas::<T>::try_mutate(383 AssetId::ForeignAssetId(foreign_asset_id),384 |maybe_asset_metadatas| -> DispatchResult {385 ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);386 *maybe_asset_metadatas = Some(metadata.clone());387 Ok(())388 },389 )390 },391 )?;392393 AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {394 *collection_id = Some(bounded_collection_id);395 Ok(())396 })397 })?;398399 Ok(foreign_asset_id)400 }401402 fn do_update_foreign_asset(403 foreign_asset_id: ForeignAssetId,404 location: &MultiLocation,405 metadata: &AssetMetadata<BalanceOf<T>>,406 ) -> DispatchResult {407 ForeignAssetLocations::<T>::try_mutate(408 foreign_asset_id,409 |maybe_multi_locations| -> DispatchResult {410 let old_multi_locations = maybe_multi_locations411 .as_mut()412 .ok_or(Error::<T>::AssetIdNotExists)?;413414 AssetMetadatas::<T>::try_mutate(415 AssetId::ForeignAssetId(foreign_asset_id),416 |maybe_asset_metadatas| -> DispatchResult {417 ensure!(418 maybe_asset_metadatas.is_some(),419 Error::<T>::AssetIdNotExists420 );421422 423 if location != old_multi_locations {424 LocationToCurrencyIds::<T>::remove(*old_multi_locations);425 LocationToCurrencyIds::<T>::try_mutate(426 location,427 |maybe_currency_ids| -> DispatchResult {428 ensure!(429 maybe_currency_ids.is_none(),430 Error::<T>::MultiLocationExisted431 );432 433 *maybe_currency_ids = Some(foreign_asset_id);434 Ok(())435 },436 )?;437 }438 *maybe_asset_metadatas = Some(metadata.clone());439 *old_multi_locations = *location;440 Ok(())441 },442 )443 },444 )445 }446}447448pub use frame_support::{449 traits::{450 fungibles::Balanced, tokens::currency::Currency as CurrencyT, OnUnbalanced as OnUnbalancedT,451 },452 weights::{WeightToFee, WeightToFeePolynomial},453};454455pub struct FreeForAll<456 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,457 AssetId: Get<MultiLocation>,458 AccountId,459 Currency: CurrencyT<AccountId>,460 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,461>(462 Weight,463 Currency::Balance,464 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,465);466467impl<468 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,469 AssetId: Get<MultiLocation>,470 AccountId,471 Currency: CurrencyT<AccountId>,472 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,473 > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>474{475 fn new() -> Self {476 Self(Weight::default(), Zero::zero(), PhantomData)477 }478479 fn buy_weight(480 &mut self,481 weight: Weight,482 payment: Assets,483 _xcm: &XcmContext,484 ) -> Result<Assets, XcmError> {485 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);486 Ok(payment)487 }488}489impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop490 for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>491where492 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,493 AssetId: Get<MultiLocation>,494 Currency: CurrencyT<AccountId>,495 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,496{497 fn drop(&mut self) {498 OnUnbalanced::on_unbalanced(Currency::issue(self.1));499 }500}