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