12345678910111213141516171819use frame_support::traits::tokens::{20 DepositConsequence, Fortitude, Precision, Preservation, Provenance, WithdrawConsequence,21};22use frame_system::Config as SystemConfig;23use pallet_common::{CollectionHandle, CommonCollectionOperations};24use pallet_fungible::FungibleHandle;25use sp_runtime::traits::{CheckedAdd, CheckedSub};26use up_data_structs::budget;2728use super::*;2930impl<T: Config> fungibles::Inspect<<T as SystemConfig>::AccountId> for Pallet<T>31where32 T: orml_tokens::Config<CurrencyId = AssetId>,33 BalanceOf<T>: From<<T as pallet_balances::Config>::Balance>,34 BalanceOf<T>: From<<T as orml_tokens::Config>::Balance>,35 <T as pallet_balances::Config>::Balance: From<BalanceOf<T>>,36 <T as orml_tokens::Config>::Balance: From<BalanceOf<T>>,37{38 type AssetId = AssetId;39 type Balance = BalanceOf<T>;4041 fn total_issuance(asset: Self::AssetId) -> Self::Balance {42 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible total_issuance");4344 match asset {45 AssetId::NativeAssetId(NativeCurrency::Here) => {46 <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::total_issuance()47 .into()48 }49 AssetId::NativeAssetId(NativeCurrency::Parent) => {50 <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::total_issuance(51 AssetId::NativeAssetId(NativeCurrency::Parent),52 )53 .into()54 }55 AssetId::ForeignAssetId(fid) => {56 let target_collection_id = match <AssetBinding<T>>::get(fid) {57 Some(v) => v,58 None => return Zero::zero(),59 };60 let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {61 Ok(v) => v,62 Err(_) => return Zero::zero(),63 };64 let collection = FungibleHandle::cast(collection_handle);65 Self::Balance::try_from(collection.total_supply()).unwrap_or(Zero::zero())66 }67 }68 }6970 fn minimum_balance(asset: Self::AssetId) -> Self::Balance {71 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible minimum_balance");72 match asset {73 AssetId::NativeAssetId(NativeCurrency::Here) => {74 <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::minimum_balance()75 .into()76 }77 AssetId::NativeAssetId(NativeCurrency::Parent) => {78 <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::minimum_balance(79 AssetId::NativeAssetId(NativeCurrency::Parent),80 )81 .into()82 }83 AssetId::ForeignAssetId(fid) => AssetMetadatas::<T>::get(AssetId::ForeignAssetId(fid))84 .map(|x| x.minimal_balance)85 .unwrap_or_else(Zero::zero),86 }87 }8889 fn balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {90 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible balance");91 match asset {92 AssetId::NativeAssetId(NativeCurrency::Here) => {93 <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::balance(who).into()94 }95 AssetId::NativeAssetId(NativeCurrency::Parent) => {96 <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::balance(97 AssetId::NativeAssetId(NativeCurrency::Parent),98 who,99 )100 .into()101 }102 AssetId::ForeignAssetId(fid) => {103 let target_collection_id = match <AssetBinding<T>>::get(fid) {104 Some(v) => v,105 None => return Zero::zero(),106 };107 let collection_handle = match <CollectionHandle<T>>::try_get(target_collection_id) {108 Ok(v) => v,109 Err(_) => return Zero::zero(),110 };111 let collection = FungibleHandle::cast(collection_handle);112 Self::Balance::try_from(113 collection.balance(T::CrossAccountId::from_sub(who.clone()), TokenId(0)),114 )115 .unwrap_or(Zero::zero())116 }117 }118 }119120 fn total_balance(asset: Self::AssetId, who: &<T as SystemConfig>::AccountId) -> Self::Balance {121 Self::balance(asset, who)122 }123124 fn reducible_balance(125 asset: Self::AssetId,126 who: &<T as SystemConfig>::AccountId,127 preservation: Preservation,128 fortitude: Fortitude,129 ) -> Self::Balance {130 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible reducible_balance");131132 match asset {133 AssetId::NativeAssetId(NativeCurrency::Here) => {134 <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::reducible_balance(135 who,136 preservation,137 fortitude,138 )139 .into()140 }141 AssetId::NativeAssetId(NativeCurrency::Parent) => {142 <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::reducible_balance(143 AssetId::NativeAssetId(NativeCurrency::Parent),144 who,145 preservation,146 fortitude,147 )148 .into()149 }150 _ => Self::balance(asset, who),151 }152 }153154 fn can_deposit(155 asset: Self::AssetId,156 who: &<T as SystemConfig>::AccountId,157 amount: Self::Balance,158 provenance: Provenance,159 ) -> DepositConsequence {160 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible can_deposit");161162 match asset {163 AssetId::NativeAssetId(NativeCurrency::Here) => {164 <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_deposit(165 who,166 amount.into(),167 provenance,168 )169 }170 AssetId::NativeAssetId(NativeCurrency::Parent) => {171 <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_deposit(172 AssetId::NativeAssetId(NativeCurrency::Parent),173 who,174 amount.into(),175 provenance,176 )177 }178 _ => {179 if amount.is_zero() {180 return DepositConsequence::Success;181 }182183 let extential_deposit_value = T::ExistentialDeposit::get();184 let ed_value: u128 = match extential_deposit_value.try_into() {185 Ok(val) => val,186 Err(_) => return DepositConsequence::CannotCreate,187 };188 let extential_deposit: Self::Balance = match ed_value.try_into() {189 Ok(val) => val,190 Err(_) => return DepositConsequence::CannotCreate,191 };192193 let new_total_balance = match Self::balance(asset, who).checked_add(&amount) {194 Some(x) => x,195 None => return DepositConsequence::Overflow,196 };197198 if new_total_balance < extential_deposit {199 return DepositConsequence::BelowMinimum;200 }201202 DepositConsequence::Success203 }204 }205 }206207 fn can_withdraw(208 asset: Self::AssetId,209 who: &<T as SystemConfig>::AccountId,210 amount: Self::Balance,211 ) -> WithdrawConsequence<Self::Balance> {212 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible can_withdraw");213 let value: u128 = match amount.try_into() {214 Ok(val) => val,215 Err(_) => return WithdrawConsequence::UnknownAsset,216 };217218 match asset {219 AssetId::NativeAssetId(NativeCurrency::Here) => {220 let this_amount: <T as pallet_balances::Config>::Balance = match value.try_into() {221 Ok(val) => val,222 Err(_) => {223 return WithdrawConsequence::UnknownAsset;224 }225 };226 match <pallet_balances::Pallet<T> as fungible::Inspect<T::AccountId>>::can_withdraw(227 who,228 this_amount,229 ) {230 WithdrawConsequence::BalanceLow => WithdrawConsequence::BalanceLow,231 WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,232 WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,233 WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,234 WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,235 WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,236 WithdrawConsequence::Success => WithdrawConsequence::Success,237 _ => WithdrawConsequence::BalanceLow,238 }239 }240 AssetId::NativeAssetId(NativeCurrency::Parent) => {241 let parent_amount: <T as orml_tokens::Config>::Balance = match value.try_into() {242 Ok(val) => val,243 Err(_) => {244 return WithdrawConsequence::UnknownAsset;245 }246 };247 match <orml_tokens::Pallet<T> as fungibles::Inspect<T::AccountId>>::can_withdraw(248 AssetId::NativeAssetId(NativeCurrency::Parent),249 who,250 parent_amount,251 ) {252 WithdrawConsequence::BalanceLow => WithdrawConsequence::BalanceLow,253 WithdrawConsequence::WouldDie => WithdrawConsequence::WouldDie,254 WithdrawConsequence::UnknownAsset => WithdrawConsequence::UnknownAsset,255 WithdrawConsequence::Underflow => WithdrawConsequence::Underflow,256 WithdrawConsequence::Overflow => WithdrawConsequence::Overflow,257 WithdrawConsequence::Frozen => WithdrawConsequence::Frozen,258 WithdrawConsequence::Success => WithdrawConsequence::Success,259 _ => WithdrawConsequence::BalanceLow,260 }261 }262 _ => match Self::balance(asset, who).checked_sub(&amount) {263 Some(_) => WithdrawConsequence::Success,264 None => WithdrawConsequence::BalanceLow,265 },266 }267 }268269 fn asset_exists(asset: AssetId) -> bool {270 match asset {271 AssetId::NativeAssetId(_) => true,272 AssetId::ForeignAssetId(fid) => <AssetBinding<T>>::contains_key(fid),273 }274 }275}276277impl<T: Config> fungibles::Mutate<<T as SystemConfig>::AccountId> for Pallet<T>278where279 T: orml_tokens::Config<CurrencyId = AssetId>,280 BalanceOf<T>: From<<T as pallet_balances::Config>::Balance>,281 BalanceOf<T>: From<<T as orml_tokens::Config>::Balance>,282 <T as pallet_balances::Config>::Balance: From<BalanceOf<T>>,283 <T as orml_tokens::Config>::Balance: From<BalanceOf<T>>,284 u128: From<BalanceOf<T>>,285{286 fn mint_into(287 asset: Self::AssetId,288 who: &<T as SystemConfig>::AccountId,289 amount: Self::Balance,290 ) -> Result<BalanceOf<T>, DispatchError> {291 292 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible mint_into {:?}", asset);293294 match asset {295 AssetId::NativeAssetId(NativeCurrency::Here) => {296 <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::mint_into(297 who,298 amount.into(),299 )300 .map(Into::into)301 }302 AssetId::NativeAssetId(NativeCurrency::Parent) => {303 <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::mint_into(304 AssetId::NativeAssetId(NativeCurrency::Parent),305 who,306 amount.into(),307 )308 .map(Into::into)309 }310 AssetId::ForeignAssetId(fid) => {311 let target_collection_id = match <AssetBinding<T>>::get(fid) {312 Some(v) => v,313 None => {314 return Err(DispatchError::Other(315 "Associated collection not found for asset",316 ))317 }318 };319 let collection =320 FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);321 let account = T::CrossAccountId::from_sub(who.clone());322323 let amount_data: pallet_fungible::CreateItemData<T> =324 (account.clone(), amount.into());325326 pallet_fungible::Pallet::<T>::create_item_foreign(327 &collection,328 &account,329 amount_data,330 &budget::Value::new(0),331 )?;332333 Ok(amount)334 }335 }336 }337338 fn burn_from(339 asset: Self::AssetId,340 who: &<T as SystemConfig>::AccountId,341 amount: Self::Balance,342 precision: Precision,343 fortitude: Fortitude,344 ) -> Result<Self::Balance, DispatchError> {345 346 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible burn_from");347348 match asset {349 AssetId::NativeAssetId(NativeCurrency::Here) => {350 <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::burn_from(351 who,352 amount.into(),353 precision,354 fortitude,355 )356 .map(Into::into)357 }358 AssetId::NativeAssetId(NativeCurrency::Parent) => {359 <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::burn_from(360 AssetId::NativeAssetId(NativeCurrency::Parent),361 who,362 amount.into(),363 precision,364 fortitude,365 )366 .map(Into::into)367 }368 AssetId::ForeignAssetId(fid) => {369 let target_collection_id = match <AssetBinding<T>>::get(fid) {370 Some(v) => v,371 None => {372 return Err(DispatchError::Other(373 "Associated collection not found for asset",374 ))375 }376 };377 let collection =378 FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);379 pallet_fungible::Pallet::<T>::burn_foreign(380 &collection,381 &T::CrossAccountId::from_sub(who.clone()),382 amount.into(),383 )?;384385 Ok(amount)386 }387 }388 }389390 fn transfer(391 asset: Self::AssetId,392 source: &<T as SystemConfig>::AccountId,393 dest: &<T as SystemConfig>::AccountId,394 amount: Self::Balance,395 preservation: Preservation,396 ) -> Result<Self::Balance, DispatchError> {397 398 log::trace!(target: "fassets::impl_foreign_assets", "impl_fungible transfer");399400 match asset {401 AssetId::NativeAssetId(NativeCurrency::Here) => {402 match <pallet_balances::Pallet<T> as fungible::Mutate<T::AccountId>>::transfer(403 source,404 dest,405 amount.into(),406 preservation,407 ) {408 Ok(_) => Ok(amount),409 Err(_) => Err(DispatchError::Other(410 "Bad amount to relay chain value conversion",411 )),412 }413 }414 AssetId::NativeAssetId(NativeCurrency::Parent) => {415 match <orml_tokens::Pallet<T> as fungibles::Mutate<T::AccountId>>::transfer(416 AssetId::NativeAssetId(NativeCurrency::Parent),417 source,418 dest,419 amount.into(),420 preservation,421 ) {422 Ok(_) => Ok(amount),423 Err(e) => Err(e),424 }425 }426 AssetId::ForeignAssetId(fid) => {427 let target_collection_id = match <AssetBinding<T>>::get(fid) {428 Some(v) => v,429 None => {430 return Err(DispatchError::Other(431 "Associated collection not found for asset",432 ))433 }434 };435 let collection =436 FungibleHandle::cast(<CollectionHandle<T>>::try_get(target_collection_id)?);437438 pallet_fungible::Pallet::<T>::transfer(439 &collection,440 &T::CrossAccountId::from_sub(source.clone()),441 &T::CrossAccountId::from_sub(dest.clone()),442 amount.into(),443 &budget::Value::new(0),444 )445 .map_err(|e| e.error)?;446447 Ok(amount)448 }449 }450 }451}452453#[cfg(not(debug_assertions))]454extern "C" {455 456 457 458 459 460 461 462 463 fn unbalanced_fungible_is_called();464}465macro_rules! ensure_balanced {466 () => {{467 #[cfg(debug_assertions)]468 panic!("unbalanced fungible methods should not be used");469 #[cfg(not(debug_assertions))]470 {471 unsafe { unbalanced_fungible_is_called() };472 unreachable!();473 }474 }};475}476477impl<T: Config> fungibles::Unbalanced<<T as SystemConfig>::AccountId> for Pallet<T>478where479 T: orml_tokens::Config<CurrencyId = AssetId>,480 BalanceOf<T>: From<<T as pallet_balances::Config>::Balance>,481 BalanceOf<T>: From<<T as orml_tokens::Config>::Balance>,482 <T as pallet_balances::Config>::Balance: From<BalanceOf<T>>,483 <T as orml_tokens::Config>::Balance: From<BalanceOf<T>>,484 u128: From<BalanceOf<T>>,485{486 fn handle_dust(_dust: fungibles::Dust<<T as SystemConfig>::AccountId, Self>) {487 ensure_balanced!();488 }489 fn write_balance(490 _asset: Self::AssetId,491 _who: &<T as SystemConfig>::AccountId,492 _amount: Self::Balance,493 ) -> Result<Option<Self::Balance>, DispatchError> {494 ensure_balanced!();495 }496 fn set_total_issuance(_asset: Self::AssetId, _amount: Self::Balance) {497 ensure_balanced!();498 }499}