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

difftreelog

source

pallets/fungible/src/lib.rs9.7 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::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: BTreeMap<T::CrossAccountId, u128>,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 total_supply = data240			.iter()241			.map(|(_, v)| *v)242			.try_fold(<TotalSupply<T>>::get(collection.id), |acc, v| {243				acc.checked_add(v)244			})245			.ok_or(ArithmeticError::Overflow)?;246247		let mut balances = data;248		for (k, v) in balances.iter_mut() {249			*v = <Balance<T>>::get((collection.id, &k))250				.checked_add(*v)251				.ok_or(ArithmeticError::Overflow)?;252		}253254		// =========255256		<TotalSupply<T>>::insert(collection.id, total_supply);257		for (user, amount) in balances {258			<Balance<T>>::insert((collection.id, &user), amount);259260			collection.log_mirrored(ERC20Events::Transfer {261				from: H160::default(),262				to: *user.as_eth(),263				value: amount.into(),264			});265			<PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(266				collection.id,267				TokenId::default(),268				user.clone(),269				amount,270			));271		}272273		Ok(())274	}275276	fn set_allowance_unchecked(277		collection: &FungibleHandle<T>,278		owner: &T::CrossAccountId,279		spender: &T::CrossAccountId,280		amount: u128,281	) {282		if amount == 0 {283			<Allowance<T>>::remove((collection.id, owner, spender));284		} else {285			<Allowance<T>>::insert((collection.id, owner, spender), amount);286		}287288		collection.log_mirrored(ERC20Events::Approval {289			owner: *owner.as_eth(),290			spender: *spender.as_eth(),291			value: amount.into(),292		});293		<PalletCommon<T>>::deposit_event(CommonEvent::Approved(294			collection.id,295			TokenId(0),296			owner.clone(),297			spender.clone(),298			amount,299		));300	}301302	pub fn set_allowance(303		collection: &FungibleHandle<T>,304		owner: &T::CrossAccountId,305		spender: &T::CrossAccountId,306		amount: u128,307	) -> DispatchResult {308		if collection.access == AccessMode::AllowList {309			collection.check_allowlist(owner)?;310			collection.check_allowlist(spender)?;311		}312313		if <Balance<T>>::get((collection.id, owner)) < amount {314			ensure!(315				collection.ignores_owned_amount(owner),316				<CommonError<T>>::CantApproveMoreThanOwned317			);318		}319320		// =========321322		Self::set_allowance_unchecked(collection, owner, spender, amount);323		Ok(())324	}325326	pub fn transfer_from(327		collection: &FungibleHandle<T>,328		spender: &T::CrossAccountId,329		from: &T::CrossAccountId,330		to: &T::CrossAccountId,331		amount: u128,332	) -> DispatchResult {333		if spender.conv_eq(from) {334			return Self::transfer(collection, from, to, amount);335		}336		if collection.access == AccessMode::AllowList {337			// `from`, `to` checked in [`transfer`]338			collection.check_allowlist(spender)?;339		}340341		let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);342		if allowance.is_none() {343			ensure!(344				collection.ignores_allowance(spender),345				<CommonError<T>>::ApprovedValueTooLow346			);347		}348349		// =========350351		Self::transfer(collection, from, to, amount)?;352		if let Some(allowance) = allowance {353			Self::set_allowance_unchecked(collection, from, spender, allowance);354		}355		Ok(())356	}357358	pub fn burn_from(359		collection: &FungibleHandle<T>,360		spender: &T::CrossAccountId,361		from: &T::CrossAccountId,362		amount: u128,363	) -> DispatchResult {364		if spender.conv_eq(from) {365			return Self::burn(collection, from, amount);366		}367		if collection.access == AccessMode::AllowList {368			// `from` checked in [`burn`]369			collection.check_allowlist(spender)?;370		}371372		let allowance = <Allowance<T>>::get((collection.id, from, spender)).checked_sub(amount);373		if allowance.is_none() {374			ensure!(375				collection.ignores_allowance(spender),376				<CommonError<T>>::ApprovedValueTooLow377			);378		}379380		// =========381382		Self::burn(collection, from, amount)?;383		if let Some(allowance) = allowance {384			Self::set_allowance_unchecked(collection, from, spender, allowance);385		}386		Ok(())387	}388389	/// Delegated to `create_multiple_items`390	pub fn create_item(391		collection: &FungibleHandle<T>,392		sender: &T::CrossAccountId,393		data: CreateItemData<T>,394	) -> DispatchResult {395		Self::create_multiple_items(collection, sender, [(data.0, data.1)].into_iter().collect())396	}397}