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 RuntimeDebug,43};44use frame_system::pallet_prelude::*;45use up_data_structs::{CollectionMode};46use pallet_fungible::{Pallet as PalletFungible};47use scale_info::{TypeInfo};48use sp_runtime::{49 traits::{One, Zero},50 ArithmeticError,51};52use sp_std::{boxed::Box, vec::Vec};53use up_data_structs::{CollectionId, TokenId, CreateCollectionData};54555657use xcm::opaque::latest::{prelude::XcmError, Weight};58use xcm::{latest::MultiLocation, VersionedMultiLocation};59use xcm_executor::{traits::WeightTrader, Assets};6061use pallet_common::erc::CrossAccountId;6263#[cfg(feature = "std")]64use serde::{Deserialize, Serialize};656667686970#[derive(71 Clone,72 Copy,73 Eq,74 PartialEq,75 PartialOrd,76 Ord,77 MaxEncodedLen,78 RuntimeDebug,79 Encode,80 Decode,81 TypeInfo,82)]83#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]84pub enum NativeCurrency {85 Here = 0,86 Parent = 1,87}8889#[derive(90 Clone,91 Copy,92 Eq,93 PartialEq,94 PartialOrd,95 Ord,96 MaxEncodedLen,97 RuntimeDebug,98 Encode,99 Decode,100 TypeInfo,101)]102#[cfg_attr(feature = "std", derive(Serialize, Deserialize))]103pub enum AssetIds {104 ForeignAssetId(ForeignAssetId),105 NativeAssetId(NativeCurrency),106}107108pub trait TryAsForeign<T, F> {109 fn try_as_foreign(asset: T) -> Option<F>;110}111112impl TryAsForeign<AssetIds, ForeignAssetId> for AssetIds {113 fn try_as_foreign(asset: AssetIds) -> Option<ForeignAssetId> {114 match asset {115 AssetIds::ForeignAssetId(id) => Some(id),116 _ => None,117 }118 }119}120121pub type ForeignAssetId = u32;122pub type CurrencyId = AssetIds;123124mod impl_fungibles;125pub mod weights;126127#[cfg(feature = "runtime-benchmarks")]128mod benchmarking;129130pub use module::*;131pub use weights::WeightInfo;132133134pub type BalanceOf<T> =135 <<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;136137138pub trait AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata> {139 140 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata>;141 142 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation>;143 144 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId>;145}146147pub struct XcmForeignAssetIdMapping<T>(sp_std::marker::PhantomData<T>);148149impl<T: Config> AssetIdMapping<ForeignAssetId, MultiLocation, AssetMetadata<BalanceOf<T>>>150 for XcmForeignAssetIdMapping<T>151{152 fn get_asset_metadata(foreign_asset_id: ForeignAssetId) -> Option<AssetMetadata<BalanceOf<T>>> {153 log::trace!(target: "fassets::asset_metadatas", "call");154 Pallet::<T>::asset_metadatas(AssetIds::ForeignAssetId(foreign_asset_id))155 }156157 fn get_multi_location(foreign_asset_id: ForeignAssetId) -> Option<MultiLocation> {158 log::trace!(target: "fassets::get_multi_location", "call");159 Pallet::<T>::foreign_asset_locations(foreign_asset_id)160 }161162 fn get_currency_id(multi_location: MultiLocation) -> Option<CurrencyId> {163 log::trace!(target: "fassets::get_currency_id", "call");164 Pallet::<T>::location_to_currency_ids(multi_location).map(|id| AssetIds::ForeignAssetId(id))165 }166}167168#[frame_support::pallet]169pub mod module {170 use super::*;171172 #[pallet::config]173 pub trait Config:174 frame_system::Config175 + pallet_common::Config176 + pallet_fungible::Config177 + orml_tokens::Config178 + pallet_balances::Config179 {180 181 type RuntimeEvent: From<Event<Self>> + IsType<<Self as frame_system::Config>::RuntimeEvent>;182183 184 type Currency: Currency<Self::AccountId>;185186 187 type RegisterOrigin: EnsureOrigin<Self::RuntimeOrigin>;188189 190 type WeightInfo: WeightInfo;191 }192193 #[derive(Clone, Eq, PartialEq, RuntimeDebug, Encode, Decode, TypeInfo)]194 pub struct AssetMetadata<Balance> {195 pub name: Vec<u8>,196 pub symbol: Vec<u8>,197 pub decimals: u8,198 pub minimal_balance: Balance,199 }200201 #[pallet::error]202 pub enum Error<T> {203 204 205 BadLocation,206 207 MultiLocationExisted,208 209 AssetIdNotExists,210 211 AssetIdExisted,212 }213214 #[pallet::event]215 #[pallet::generate_deposit(fn deposit_event)]216 pub enum Event<T: Config> {217 218 ForeignAssetRegistered {219 asset_id: ForeignAssetId,220 asset_address: MultiLocation,221 metadata: AssetMetadata<BalanceOf<T>>,222 },223 224 ForeignAssetUpdated {225 asset_id: ForeignAssetId,226 asset_address: MultiLocation,227 metadata: AssetMetadata<BalanceOf<T>>,228 },229 230 AssetRegistered {231 asset_id: AssetIds,232 metadata: AssetMetadata<BalanceOf<T>>,233 },234 235 AssetUpdated {236 asset_id: AssetIds,237 metadata: AssetMetadata<BalanceOf<T>>,238 },239 }240241 242 243 244 #[pallet::storage]245 #[pallet::getter(fn next_foreign_asset_id)]246 pub type NextForeignAssetId<T: Config> = StorageValue<_, ForeignAssetId, ValueQuery>;247 248 249 250 #[pallet::storage]251 #[pallet::getter(fn foreign_asset_locations)]252 pub type ForeignAssetLocations<T: Config> =253 StorageMap<_, Twox64Concat, ForeignAssetId, xcm::v3::MultiLocation, OptionQuery>;254255 256 257 258 #[pallet::storage]259 #[pallet::getter(fn location_to_currency_ids)]260 pub type LocationToCurrencyIds<T: Config> =261 StorageMap<_, Twox64Concat, xcm::v3::MultiLocation, ForeignAssetId, OptionQuery>;262263 264 265 266 #[pallet::storage]267 #[pallet::getter(fn asset_metadatas)]268 pub type AssetMetadatas<T: Config> =269 StorageMap<_, Twox64Concat, AssetIds, AssetMetadata<BalanceOf<T>>, OptionQuery>;270271 272 273 #[pallet::storage]274 #[pallet::getter(fn asset_binding)]275 pub type AssetBinding<T: Config> =276 StorageMap<_, Twox64Concat, ForeignAssetId, CollectionId, OptionQuery>;277278 #[pallet::pallet]279 #[pallet::without_storage_info]280 pub struct Pallet<T>(_);281282 pub const STORAGE_VERSION: StorageVersion = StorageVersion::new(1);283 #[pallet::hooks]284 impl<T: Config> Hooks<BlockNumberFor<T>> for Pallet<T> {285 fn on_runtime_upgrade() -> Weight {286 let mut weight = Weight::default();287 288 if StorageVersion::get::<Pallet<T>>() <= 0 {289 pub trait V0ToV1 {290 type Pallet: 'static + PalletInfoAccess;291 }292 #[frame_support::storage_alias]293 type LocationToCurrencyIds<T: Config> =294 StorageMap<Pallet<T>, Twox64Concat, xcm::v2::MultiLocation, ForeignAssetId>;295 <ForeignAssetLocations<T>>::translate_values::<xcm::v2::MultiLocation, _>(|loc| {296 weight += T::DbWeight::get().reads_writes(1, 1);297 Some(298 xcm::v3::MultiLocation::try_from(loc)299 .expect("failed to migrate multilocation from XCMv2 to XCMv3"),300 )301 });302303 let old_values: Vec<(xcm::v2::MultiLocation, _)> =304 <LocationToCurrencyIds<T>>::drain().collect();305 weight += T::DbWeight::get()306 .reads_writes(old_values.len() as u64, old_values.len() as u64);307 for (loc, asset_id) in old_values {308 let loc = xcm::v3::MultiLocation::try_from(loc)309 .expect("failed to migrate multilocation from XCMv2 to XCMv3");310 <crate::LocationToCurrencyIds<T>>::insert(loc, asset_id);311 }312 }313 STORAGE_VERSION.put::<Pallet<T>>();314 weight += T::DbWeight::get().writes(1);315 weight316 }317 }318319 #[pallet::genesis_config]320 pub struct GenesisConfig;321322 #[cfg(feature = "std")]323 impl Default for GenesisConfig {324 fn default() -> Self {325 Self326 }327 }328329 #[pallet::genesis_build]330 impl<T: Config> GenesisBuild<T> for GenesisConfig {331 fn build(&self) {332 STORAGE_VERSION.put::<Pallet<T>>();333 }334 }335336 #[pallet::call]337 impl<T: Config> Pallet<T> {338 #[pallet::call_index(0)]339 #[pallet::weight(<T as Config>::WeightInfo::register_foreign_asset())]340 pub fn register_foreign_asset(341 origin: OriginFor<T>,342 owner: T::AccountId,343 location: Box<VersionedMultiLocation>,344 metadata: Box<AssetMetadata<BalanceOf<T>>>,345 ) -> DispatchResult {346 T::RegisterOrigin::ensure_origin(origin.clone())?;347348 let location: MultiLocation = (*location)349 .try_into()350 .map_err(|()| Error::<T>::BadLocation)?;351352 let md = metadata.clone();353 let name: Vec<u16> = md.name.into_iter().map(|x| x as u16).collect::<Vec<u16>>();354 let mut description: Vec<u16> = "Foreign assets collection for "355 .encode_utf16()356 .collect::<Vec<u16>>();357 description.append(&mut name.clone());358359 let data: CreateCollectionData<T::AccountId> = CreateCollectionData {360 name: name.try_into().unwrap(),361 description: description.try_into().unwrap(),362 mode: CollectionMode::Fungible(md.decimals),363 ..Default::default()364 };365 let owner = T::CrossAccountId::from_sub(owner);366 let bounded_collection_id =367 <PalletFungible<T>>::init_foreign_collection(owner.clone(), owner, data)?;368 let foreign_asset_id =369 Self::do_register_foreign_asset(&location, &metadata, bounded_collection_id)?;370371 Self::deposit_event(Event::<T>::ForeignAssetRegistered {372 asset_id: foreign_asset_id,373 asset_address: location,374 metadata: *metadata,375 });376 Ok(())377 }378379 #[pallet::call_index(1)]380 #[pallet::weight(<T as Config>::WeightInfo::update_foreign_asset())]381 pub fn update_foreign_asset(382 origin: OriginFor<T>,383 foreign_asset_id: ForeignAssetId,384 location: Box<VersionedMultiLocation>,385 metadata: Box<AssetMetadata<BalanceOf<T>>>,386 ) -> DispatchResult {387 T::RegisterOrigin::ensure_origin(origin)?;388389 let location: MultiLocation = (*location)390 .try_into()391 .map_err(|()| Error::<T>::BadLocation)?;392 Self::do_update_foreign_asset(foreign_asset_id, &location, &metadata)?;393394 Self::deposit_event(Event::<T>::ForeignAssetUpdated {395 asset_id: foreign_asset_id,396 asset_address: location,397 metadata: *metadata,398 });399 Ok(())400 }401 }402}403404impl<T: Config> Pallet<T> {405 fn get_next_foreign_asset_id() -> Result<ForeignAssetId, DispatchError> {406 NextForeignAssetId::<T>::try_mutate(|current| -> Result<ForeignAssetId, DispatchError> {407 let id = *current;408 *current = current409 .checked_add(One::one())410 .ok_or(ArithmeticError::Overflow)?;411 Ok(id)412 })413 }414415 fn do_register_foreign_asset(416 location: &MultiLocation,417 metadata: &AssetMetadata<BalanceOf<T>>,418 bounded_collection_id: CollectionId,419 ) -> Result<ForeignAssetId, DispatchError> {420 let foreign_asset_id = Self::get_next_foreign_asset_id()?;421 LocationToCurrencyIds::<T>::try_mutate(location, |maybe_currency_ids| -> DispatchResult {422 ensure!(423 maybe_currency_ids.is_none(),424 Error::<T>::MultiLocationExisted425 );426 *maybe_currency_ids = Some(foreign_asset_id);427 428429 ForeignAssetLocations::<T>::try_mutate(430 foreign_asset_id,431 |maybe_location| -> DispatchResult {432 ensure!(maybe_location.is_none(), Error::<T>::MultiLocationExisted);433 *maybe_location = Some(location.clone());434435 AssetMetadatas::<T>::try_mutate(436 AssetIds::ForeignAssetId(foreign_asset_id),437 |maybe_asset_metadatas| -> DispatchResult {438 ensure!(maybe_asset_metadatas.is_none(), Error::<T>::AssetIdExisted);439 *maybe_asset_metadatas = Some(metadata.clone());440 Ok(())441 },442 )443 },444 )?;445446 AssetBinding::<T>::try_mutate(foreign_asset_id, |collection_id| -> DispatchResult {447 *collection_id = Some(bounded_collection_id);448 Ok(())449 })450 })?;451452 Ok(foreign_asset_id)453 }454455 fn do_update_foreign_asset(456 foreign_asset_id: ForeignAssetId,457 location: &MultiLocation,458 metadata: &AssetMetadata<BalanceOf<T>>,459 ) -> DispatchResult {460 ForeignAssetLocations::<T>::try_mutate(461 foreign_asset_id,462 |maybe_multi_locations| -> DispatchResult {463 let old_multi_locations = maybe_multi_locations464 .as_mut()465 .ok_or(Error::<T>::AssetIdNotExists)?;466467 AssetMetadatas::<T>::try_mutate(468 AssetIds::ForeignAssetId(foreign_asset_id),469 |maybe_asset_metadatas| -> DispatchResult {470 ensure!(471 maybe_asset_metadatas.is_some(),472 Error::<T>::AssetIdNotExists473 );474475 476 if location != old_multi_locations {477 LocationToCurrencyIds::<T>::remove(old_multi_locations.clone());478 LocationToCurrencyIds::<T>::try_mutate(479 location,480 |maybe_currency_ids| -> DispatchResult {481 ensure!(482 maybe_currency_ids.is_none(),483 Error::<T>::MultiLocationExisted484 );485 486 *maybe_currency_ids = Some(foreign_asset_id);487 Ok(())488 },489 )?;490 }491 *maybe_asset_metadatas = Some(metadata.clone());492 *old_multi_locations = location.clone();493 Ok(())494 },495 )496 },497 )498 }499}500501pub use frame_support::{502 traits::{503 fungibles::{Balanced, CreditOf},504 tokens::currency::Currency as CurrencyT,505 OnUnbalanced as OnUnbalancedT,506 },507 weights::{WeightToFeePolynomial, WeightToFee},508};509510pub struct FreeForAll<511 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,512 AssetId: Get<MultiLocation>,513 AccountId,514 Currency: CurrencyT<AccountId>,515 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,516>(517 Weight,518 Currency::Balance,519 PhantomData<(WeightToFee, AssetId, AccountId, Currency, OnUnbalanced)>,520);521522impl<523 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,524 AssetId: Get<MultiLocation>,525 AccountId,526 Currency: CurrencyT<AccountId>,527 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,528 > WeightTrader for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>529{530 fn new() -> Self {531 Self(Weight::default(), Zero::zero(), PhantomData)532 }533534 fn buy_weight(&mut self, weight: Weight, payment: Assets) -> Result<Assets, XcmError> {535 log::trace!(target: "fassets::weight", "buy_weight weight: {:?}, payment: {:?}", weight, payment);536 Ok(payment)537 }538}539impl<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced> Drop540 for FreeForAll<WeightToFee, AssetId, AccountId, Currency, OnUnbalanced>541where542 WeightToFee: WeightToFeePolynomial<Balance = Currency::Balance>,543 AssetId: Get<MultiLocation>,544 Currency: CurrencyT<AccountId>,545 OnUnbalanced: OnUnbalancedT<Currency::NegativeImbalance>,546{547 fn drop(&mut self) {548 OnUnbalanced::on_unbalanced(Currency::issue(self.1));549 }550}