git.delta.rocks / unique-network / refs/commits / c9e84df54927

difftreelog

source

pallets/fungible/src/lib.rs9.8 KiBsourcehistory
1#![cfg_attr(not(feature = "std"), no_std)]23use core::ops::Deref;4use frame_support::{ensure};5use up_data_structs::{AccessMode, CollectionId, TokenId, CreateCollectionData};6use pallet_common::{7	Error as CommonError, Event as CommonEvent, Pallet as PalletCommon, account::CrossAccountId,8};9use pallet_evm_coder_substrate::WithRecorder;10use sp_core::H160;11use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};12use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};1314pub use pallet::*;1516use crate::erc::ERC20Events;17#[cfg(feature = "runtime-benchmarks")]18pub mod benchmarking;19pub mod common;20pub mod erc;21pub mod weights;2223pub type CreateItemData<T> = (<T as pallet_common::Config>::CrossAccountId, u128);24pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;2526#[frame_support::pallet]27pub mod pallet {28	use frame_support::{Blake2_128, Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};29	use up_data_structs::CollectionId;30	use super::weights::WeightInfo;3132	#[pallet::error]33	pub enum Error<T> {34		/// Not Fungible item data used to mint in Fungible collection.35		NotFungibleDataUsedToMintFungibleCollectionToken,36		/// Not default id passed as TokenId argument37		FungibleItemsHaveNoId,38		/// Tried to set data for fungible item39		FungibleItemsDontHaveData,40	}4142	#[pallet::config]43	pub trait Config: frame_system::Config + pallet_common::Config {44		type WeightInfo: WeightInfo;45	}4647	#[pallet::pallet]48	#[pallet::generate_store(pub(super) trait Store)]49	pub struct Pallet<T>(_);5051	#[pallet::storage]52	pub type TotalSupply<T: Config> =53		StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u128, QueryKind = ValueQuery>;5455	#[pallet::storage]56	pub type Balance<T: Config> = StorageNMap<57		Key = (58			Key<Twox64Concat, CollectionId>,59			Key<Blake2_128Concat, T::CrossAccountId>,60		),61		Value = u128,62		QueryKind = ValueQuery,63	>;6465	#[pallet::storage]66	pub type Allowance<T: Config> = StorageNMap<67		Key = (68			Key<Twox64Concat, CollectionId>,69			Key<Blake2_128, T::CrossAccountId>,70			Key<Blake2_128Concat, T::CrossAccountId>,71		),72		Value = u128,73		QueryKind = ValueQuery,74	>;75}7677pub struct FungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);78impl<T: Config> FungibleHandle<T> {79	pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {80		Self(inner)81	}82	pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {83		self.084	}85}86impl<T: Config> WithRecorder<T> for FungibleHandle<T> {87	fn recorder(&self) -> &pallet_evm_coder_substrate::SubstrateRecorder<T> {88		self.0.recorder()89	}90	fn into_recorder(self) -> pallet_evm_coder_substrate::SubstrateRecorder<T> {91		self.0.into_recorder()92	}93}94impl<T: Config> Deref for FungibleHandle<T> {95	type Target = pallet_common::CollectionHandle<T>;9697	fn deref(&self) -> &Self::Target {98		&self.099	}100}101102impl<T: Config> Pallet<T> {103	pub fn init_collection(104		owner: T::AccountId,105		data: CreateCollectionData<T::AccountId>,106	) -> Result<CollectionId, DispatchError> {107		<PalletCommon<T>>::init_collection(owner, data)108	}109	pub fn destroy_collection(110		collection: FungibleHandle<T>,111		sender: &T::CrossAccountId,112	) -> DispatchResult {113		let id = collection.id;114115		// =========116117		PalletCommon::destroy_collection(collection.0, sender)?;118119		<TotalSupply<T>>::remove(id);120		<Balance<T>>::remove_prefix((id,), None);121		<Allowance<T>>::remove_prefix((id,), None);122		Ok(())123	}124125	pub fn burn(126		collection: &FungibleHandle<T>,127		owner: &T::CrossAccountId,128		amount: u128,129	) -> DispatchResult {130		let total_supply = <TotalSupply<T>>::get(collection.id)131			.checked_sub(amount)132			.ok_or(<CommonError<T>>::TokenValueTooLow)?;133134		let balance = <Balance<T>>::get((collection.id, owner))135			.checked_sub(amount)136			.ok_or(<CommonError<T>>::TokenValueTooLow)?;137138		if collection.access == AccessMode::AllowList {139			collection.check_allowlist(owner)?;140		}141142		// =========143144		if balance == 0 {145			<Balance<T>>::remove((collection.id, owner));146		} else {147			<Balance<T>>::insert((collection.id, owner), balance);148		}149		<TotalSupply<T>>::insert(collection.id, total_supply);150151		collection.log_mirrored(ERC20Events::Transfer {152			from: *owner.as_eth(),153			to: H160::default(),154			value: amount.into(),155		});156		<PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(157			collection.id,158			TokenId::default(),159			owner.clone(),160			amount,161		));162		Ok(())163	}164165	pub fn transfer(166		collection: &FungibleHandle<T>,167		from: &T::CrossAccountId,168		to: &T::CrossAccountId,169		amount: u128,170	) -> DispatchResult {171		ensure!(172			collection.limits.transfers_enabled(),173			<CommonError<T>>::TransferNotAllowed,174		);175176		if collection.access == AccessMode::AllowList {177			collection.check_allowlist(from)?;178			collection.check_allowlist(to)?;179		}180		<PalletCommon<T>>::ensure_correct_receiver(to)?;181182		let balance_from = <Balance<T>>::get((collection.id, from))183			.checked_sub(amount)184			.ok_or(<CommonError<T>>::TokenValueTooLow)?;185		let balance_to = if from != to {186			Some(187				<Balance<T>>::get((collection.id, to))188					.checked_add(amount)189					.ok_or(ArithmeticError::Overflow)?,190			)191		} else {192			None193		};194195		// =========196197		if let Some(balance_to) = balance_to {198			// from != to199			if balance_from == 0 {200				<Balance<T>>::remove((collection.id, from));201			} else {202				<Balance<T>>::insert((collection.id, from), balance_from);203			}204			<Balance<T>>::insert((collection.id, to), balance_to);205		}206207		collection.log_mirrored(ERC20Events::Transfer {208			from: *from.as_eth(),209			to: *to.as_eth(),210			value: amount.into(),211		});212		<PalletCommon<T>>::deposit_event(CommonEvent::Transfer(213			collection.id,214			TokenId::default(),215			from.clone(),216			to.clone(),217			amount,218		));219		Ok(())220	}221222	pub fn create_multiple_items(223		collection: &FungibleHandle<T>,224		sender: &T::CrossAccountId,225		data: Vec<CreateItemData<T>>,226	) -> DispatchResult {227		if !collection.is_owner_or_admin(sender) {228			ensure!(229				collection.mint_mode,230				<CommonError<T>>::PublicMintingNotAllowed231			);232			collection.check_allowlist(sender)?;233234			for (owner, _) in data.iter() {235				collection.check_allowlist(owner)?;236			}237		}238239		let mut balances = BTreeMap::new();240241		let total_supply = data242			.iter()243			.map(|u| u.1)244			.try_fold(<TotalSupply<T>>::get(collection.id), |acc, v| {245				acc.checked_add(v)246			})247			.ok_or(ArithmeticError::Overflow)?;248249		for (user, amount) in data.into_iter() {250			let balance = balances251				.entry(user.clone())252				.or_insert_with(|| <Balance<T>>::get((collection.id, user)));253			*balance = (*balance)254				.checked_add(amount)255				.ok_or(ArithmeticError::Overflow)?;256		}257258		// =========259260		<TotalSupply<T>>::insert(collection.id, total_supply);261		for (user, amount) in balances {262			<Balance<T>>::insert((collection.id, &user), amount);263264			collection.log_mirrored(ERC20Events::Transfer {265				from: H160::default(),266				to: *user.as_eth(),267				value: amount.into(),268			});269			<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(270				collection.id,271				TokenId::default(),272				user.clone(),273				amount,274			));275		}276277		Ok(())278	}279280	fn set_allowance_unchecked(281		collection: &FungibleHandle<T>,282		owner: &T::CrossAccountId,283		spender: &T::CrossAccountId,284		amount: u128,285	) {286		if amount == 0 {287			<Allowance<T>>::remove((collection.id, owner, spender));288		} else {289			<Allowance<T>>::insert((collection.id, owner, spender), amount);290		}291292		collection.log_mirrored(ERC20Events::Approval {293			owner: *owner.as_eth(),294			spender: *spender.as_eth(),295			value: amount.into(),296		});297		<PalletCommon<T>>::deposit_event(CommonEvent::Approved(298			collection.id,299			TokenId(0),300			owner.clone(),301			spender.clone(),302			amount,303		));304	}305306	pub fn set_allowance(307		collection: &FungibleHandle<T>,308		owner: &T::CrossAccountId,309		spender: &T::CrossAccountId,310		amount: u128,311	) -> DispatchResult {312		if collection.access == AccessMode::AllowList {313			collection.check_allowlist(owner)?;314			collection.check_allowlist(spender)?;315		}316317		if <Balance<T>>::get((collection.id, owner)) < amount {318			ensure!(319				collection.ignores_owned_amount(owner),320				<CommonError<T>>::CantApproveMoreThanOwned321			);322		}323324		// =========325326		Self::set_allowance_unchecked(collection, owner, spender, amount);327		Ok(())328	}329330	pub fn transfer_from(331		collection: &FungibleHandle<T>,332		spender: &T::CrossAccountId,333		from: &T::CrossAccountId,334		to: &T::CrossAccountId,335		amount: u128,336	) -> DispatchResult {337		if spender.conv_eq(from) {338			return Self::transfer(collection, from, to, amount);339		}340		if collection.access == AccessMode::AllowList {341			// `from`, `to` checked in [`transfer`]342			collection.check_allowlist(spender)?;343		}344345		let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);346		if allowance.is_none() {347			ensure!(348				collection.ignores_allowance(spender),349				<CommonError<T>>::ApprovedValueTooLow350			);351		}352353		// =========354355		Self::transfer(collection, from, to, amount)?;356		if let Some(allowance) = allowance {357			Self::set_allowance_unchecked(collection, from, spender, allowance);358		}359		Ok(())360	}361362	pub fn burn_from(363		collection: &FungibleHandle<T>,364		spender: &T::CrossAccountId,365		from: &T::CrossAccountId,366		amount: u128,367	) -> DispatchResult {368		if spender.conv_eq(from) {369			return Self::burn(collection, from, amount);370		}371		if collection.access == AccessMode::AllowList {372			// `from` checked in [`burn`]373			collection.check_allowlist(spender)?;374		}375376		let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);377		if allowance.is_none() {378			ensure!(379				collection.ignores_allowance(spender),380				<CommonError<T>>::ApprovedValueTooLow381			);382		}383384		// =========385386		Self::burn(collection, from, amount)?;387		if let Some(allowance) = allowance {388			Self::set_allowance_unchecked(collection, from, spender, allowance);389		}390		Ok(())391	}392393	/// Delegated to `create_multiple_items`394	pub fn create_item(395		collection: &FungibleHandle<T>,396		sender: &T::CrossAccountId,397		data: CreateItemData<T>,398	) -> DispatchResult {399		Self::create_multiple_items(collection, sender, vec![data])400	}401}