difftreelog
feat(fungible) transfer from parent token
in: master
2 files changed
pallets/fungible/Cargo.tomldiffbeforeafterboth--- a/pallets/fungible/Cargo.toml
+++ b/pallets/fungible/Cargo.toml
@@ -17,6 +17,7 @@
sp-std = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20" }
sp-core = { default-features = false, git = "https://github.com/paritytech/substrate", branch = "polkadot-v0.9.20" }
pallet-common = { default-features = false, path = '../common' }
+pallet-structure = { default-features = false, path = '../structure' }
up-data-structs = { default-features = false, path = '../../primitives/data-structs' }
evm-coder = { default-features = false, path = '../../crates/evm-coder' }
pallet-evm-coder-substrate = { default-features = false, path = '../evm-coder-substrate' }
@@ -36,6 +37,7 @@
"sp-std/std",
"up-data-structs/std",
"pallet-common/std",
+ "pallet-structure/std",
"evm-coder/std",
"ethereum/std",
"pallet-evm-coder-substrate/std",
pallets/fungible/src/lib.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617#![cfg_attr(not(feature = "std"), no_std)]1819use core::ops::Deref;20use frame_support::{ensure};21use pallet_evm::account::CrossAccountId;22use up_data_structs::{23 AccessMode, CollectionId, TokenId, CreateCollectionData, mapping::TokenAddressMapping,24};25use pallet_common::{26 Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,27 CollectionHandle, dispatch::CollectionDispatch,28};29use pallet_evm_coder_substrate::WithRecorder;30use sp_core::H160;31use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};32use sp_std::collections::btree_map::BTreeMap;3334pub use pallet::*;3536use crate::erc::ERC20Events;37#[cfg(feature = "runtime-benchmarks")]38pub mod benchmarking;39pub mod common;40pub mod erc;41pub mod weights;4243pub type CreateItemData<T> = (<T as pallet_evm::account::Config>::CrossAccountId, u128);44pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4546#[frame_support::pallet]47pub mod pallet {48 use frame_support::{Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};49 use up_data_structs::CollectionId;50 use super::weights::WeightInfo;5152 #[pallet::error]53 pub enum Error<T> {54 /// Not Fungible item data used to mint in Fungible collection.55 NotFungibleDataUsedToMintFungibleCollectionToken,56 /// Not default id passed as TokenId argument57 FungibleItemsHaveNoId,58 /// Tried to set data for fungible item59 FungibleItemsDontHaveData,60 /// Fungible token does not support nested61 FungibleDisallowsNesting,62 }6364 #[pallet::config]65 pub trait Config: frame_system::Config + pallet_common::Config {66 type WeightInfo: WeightInfo;67 }6869 #[pallet::pallet]70 #[pallet::generate_store(pub(super) trait Store)]71 pub struct Pallet<T>(_);7273 #[pallet::storage]74 pub type TotalSupply<T: Config> =75 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;7677 #[pallet::storage]78 pub type Balance<T: Config> = StorageNMap<79 Key = (80 Key<Twox64Concat, CollectionId>,81 Key<Blake2_128Concat, T::CrossAccountId>,82 ),83 Value = u128,84 QueryKind = ValueQuery,85 >;8687 #[pallet::storage]88 pub type Allowance<T: Config> = StorageNMap<89 Key = (90 Key<Twox64Concat, CollectionId>,91 Key<Blake2_128, T::CrossAccountId>,92 Key<Blake2_128Concat, T::CrossAccountId>,93 ),94 Value = u128,95 QueryKind = ValueQuery,96 >;97}9899pub struct FungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);100impl<T: Config> FungibleHandle<T> {101 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {102 Self(inner)103 }104 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {105 self.0106 }107}108impl<T: Config> WithRecorder<T> for FungibleHandle<T> {109 fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {110 self.0.recorder()111 }112 fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {113 self.0.into_recorder()114 }115}116impl<T: Config> Deref for FungibleHandle<T> {117 type Target = pallet_common::CollectionHandle<T>;118119 fn deref(&self) -> &Self::Target {120 &self.0121 }122}123124impl<T: Config> Pallet<T> {125 pub fn init_collection(126 owner: T::AccountId,127 data: CreateCollectionData<T::AccountId>,128 ) -> Result<CollectionId, DispatchError> {129 <PalletCommon<T>>::init_collection(owner, data)130 }131 pub fn destroy_collection(132 collection: FungibleHandle<T>,133 sender: &T::CrossAccountId,134 ) -> DispatchResult {135 let id = collection.id;136137 // =========138139 PalletCommon::destroy_collection(collection.0, sender)?;140141 <TotalSupply<T>>::remove(id);142 <Balance<T>>::remove_prefix((id,), None);143 <Allowance<T>>::remove_prefix((id,), None);144 Ok(())145 }146147 pub fn burn(148 collection: &FungibleHandle<T>,149 owner: &T::CrossAccountId,150 amount: u128,151 ) -> DispatchResult {152 let total_supply = <TotalSupply<T>>::get(collection.id)153 .checked_sub(amount)154 .ok_or(<CommonError<T>>::TokenValueTooLow)?;155156 let balance = <Balance<T>>::get((collection.id, owner))157 .checked_sub(amount)158 .ok_or(<CommonError<T>>::TokenValueTooLow)?;159160 if collection.access == AccessMode::AllowList {161 collection.check_allowlist(owner)?;162 }163164 // =========165166 if balance == 0 {167 <Balance<T>>::remove((collection.id, owner));168 } else {169 <Balance<T>>::insert((collection.id, owner), balance);170 }171 <TotalSupply<T>>::insert(collection.id, total_supply);172173 collection.log_mirrored(ERC20Events::Transfer {174 from: *owner.as_eth(),175 to: H160::default(),176 value: amount.into(),177 });178 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(179 collection.id,180 TokenId::default(),181 owner.clone(),182 amount,183 ));184 Ok(())185 }186187 pub fn transfer(188 collection: &FungibleHandle<T>,189 from: &T::CrossAccountId,190 to: &T::CrossAccountId,191 amount: u128,192 ) -> DispatchResult {193 ensure!(194 collection.limits.transfers_enabled(),195 <CommonError<T>>::TransferNotAllowed,196 );197198 if collection.access == AccessMode::AllowList {199 collection.check_allowlist(from)?;200 collection.check_allowlist(to)?;201 }202 <PalletCommon<T>>::ensure_correct_receiver(to)?;203204 let balance_from = <Balance<T>>::get((collection.id, from))205 .checked_sub(amount)206 .ok_or(<CommonError<T>>::TokenValueTooLow)?;207 let balance_to = if from != to {208 Some(209 <Balance<T>>::get((collection.id, to))210 .checked_add(amount)211 .ok_or(ArithmeticError::Overflow)?,212 )213 } else {214 None215 };216217 if let Some(target) = T::CrossTokenAddressMapping::address_to_token(to) {218 let handle = <CollectionHandle<T>>::try_get(target.0)?;219 let dispatch = T::CollectionDispatch::dispatch(handle);220 let dispatch = dispatch.as_dyn();221222 // =========223224 dispatch.nest_token(from.clone(), (collection.id, TokenId::default()), target.1)?;225 }226227 if let Some(balance_to) = balance_to {228 // from != to229 if balance_from == 0 {230 <Balance<T>>::remove((collection.id, from));231 } else {232 <Balance<T>>::insert((collection.id, from), balance_from);233 }234 <Balance<T>>::insert((collection.id, to), balance_to);235 }236237 collection.log_mirrored(ERC20Events::Transfer {238 from: *from.as_eth(),239 to: *to.as_eth(),240 value: amount.into(),241 });242 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(243 collection.id,244 TokenId::default(),245 from.clone(),246 to.clone(),247 amount,248 ));249 Ok(())250 }251252 pub fn create_multiple_items(253 collection: &FungibleHandle<T>,254 sender: &T::CrossAccountId,255 data: BTreeMap<T::CrossAccountId, u128>,256 ) -> DispatchResult {257 if !collection.is_owner_or_admin(sender) {258 ensure!(259 collection.mint_mode,260 <CommonError<T>>::PublicMintingNotAllowed261 );262 collection.check_allowlist(sender)?;263264 for (owner, _) in data.iter() {265 collection.check_allowlist(owner)?;266 }267 }268269 let total_supply = data270 .iter()271 .map(|(_, v)| *v)272 .try_fold(<TotalSupply<T>>::get(collection.id), |acc, v| {273 acc.checked_add(v)274 })275 .ok_or(ArithmeticError::Overflow)?;276277 let mut balances = data;278 for (k, v) in balances.iter_mut() {279 *v = <Balance<T>>::get((collection.id, &k))280 .checked_add(*v)281 .ok_or(ArithmeticError::Overflow)?;282 }283284 // =========285286 <TotalSupply<T>>::insert(collection.id, total_supply);287 for (user, amount) in balances {288 <Balance<T>>::insert((collection.id, &user), amount);289290 collection.log_mirrored(ERC20Events::Transfer {291 from: H160::default(),292 to: *user.as_eth(),293 value: amount.into(),294 });295 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(296 collection.id,297 TokenId::default(),298 user.clone(),299 amount,300 ));301 }302303 Ok(())304 }305306 fn set_allowance_unchecked(307 collection: &FungibleHandle<T>,308 owner: &T::CrossAccountId,309 spender: &T::CrossAccountId,310 amount: u128,311 ) {312 if amount == 0 {313 <Allowance<T>>::remove((collection.id, owner, spender));314 } else {315 <Allowance<T>>::insert((collection.id, owner, spender), amount);316 }317318 collection.log_mirrored(ERC20Events::Approval {319 owner: *owner.as_eth(),320 spender: *spender.as_eth(),321 value: amount.into(),322 });323 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(324 collection.id,325 TokenId(0),326 owner.clone(),327 spender.clone(),328 amount,329 ));330 }331332 pub fn set_allowance(333 collection: &FungibleHandle<T>,334 owner: &T::CrossAccountId,335 spender: &T::CrossAccountId,336 amount: u128,337 ) -> DispatchResult {338 if collection.access == AccessMode::AllowList {339 collection.check_allowlist(owner)?;340 collection.check_allowlist(spender)?;341 }342343 if <Balance<T>>::get((collection.id, owner)) < amount {344 ensure!(345 collection.ignores_owned_amount(owner),346 <CommonError<T>>::CantApproveMoreThanOwned347 );348 }349350 // =========351352 Self::set_allowance_unchecked(collection, owner, spender, amount);353 Ok(())354 }355356 pub fn transfer_from(357 collection: &FungibleHandle<T>,358 spender: &T::CrossAccountId,359 from: &T::CrossAccountId,360 to: &T::CrossAccountId,361 amount: u128,362 ) -> DispatchResult {363 if spender.conv_eq(from) {364 return Self::transfer(collection, from, to, amount);365 }366 if collection.access == AccessMode::AllowList {367 // `from`, `to` checked in [`transfer`]368 collection.check_allowlist(spender)?;369 }370371 let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);372 if allowance.is_none() {373 ensure!(374 collection.ignores_allowance(spender),375 <CommonError<T>>::ApprovedValueTooLow376 );377 }378379 // =========380381 Self::transfer(collection, from, to, amount)?;382 if let Some(allowance) = allowance {383 Self::set_allowance_unchecked(collection, from, spender, allowance);384 }385 Ok(())386 }387388 pub fn burn_from(389 collection: &FungibleHandle<T>,390 spender: &T::CrossAccountId,391 from: &T::CrossAccountId,392 amount: u128,393 ) -> DispatchResult {394 if spender.conv_eq(from) {395 return Self::burn(collection, from, amount);396 }397 if collection.access == AccessMode::AllowList {398 // `from` checked in [`burn`]399 collection.check_allowlist(spender)?;400 }401402 let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);403 if allowance.is_none() {404 ensure!(405 collection.ignores_allowance(spender),406 <CommonError<T>>::ApprovedValueTooLow407 );408 }409410 // =========411412 Self::burn(collection, from, amount)?;413 if let Some(allowance) = allowance {414 Self::set_allowance_unchecked(collection, from, spender, allowance);415 }416 Ok(())417 }418419 /// Delegated to `create_multiple_items`420 pub fn create_item(421 collection: &FungibleHandle<T>,422 sender: &T::CrossAccountId,423 data: CreateItemData<T>,424 ) -> DispatchResult {425 Self::create_multiple_items(collection, sender, [(data.0, data.1)].into_iter().collect())426 }427}