difftreelog
refactor split common pallet
in: master
16 files changed
pallets/common/Cargo.tomldiffbeforeafterboth--- /dev/null
+++ b/pallets/common/Cargo.toml
@@ -0,0 +1,35 @@
+[package]
+name = "pallet-common"
+version = "0.1.0"
+edition = "2018"
+
+[dependencies.codec]
+default-features = false
+features = ['derive']
+package = 'parity-scale-codec'
+version = '2.0.0'
+
+[dependencies]
+frame-support = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+frame-system = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+sp-runtime = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+sp-std = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+sp-core = { default-features = false, version = '4.0.0-dev', git = 'https://github.com/paritytech/substrate.git', branch = 'polkadot-v0.9.10' }
+nft-data-structs = { default-features = false, path = '../../primitives/nft' }
+pallet-evm-coder-substrate = { default-features = false, path = '../../pallets/evm-coder-substrate' }
+evm-coder = { default-features = false, path = '../../crates/evm-coder' }
+
+pallet-evm = { default-features = false, version = "6.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "unique-polkadot-v0.9.10" }
+serde = { version = "1.0.130", default-features = false }
+
+[features]
+default = ["std"]
+std = [
+ "frame-support/std",
+ "frame-system/std",
+ "sp-runtime/std",
+ "sp-std/std",
+ "nft-data-structs/std",
+ "pallet-evm/std",
+]
+runtime-benchmarks = []
pallets/common/src/account.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/account.rs
@@ -0,0 +1,142 @@
+use crate::Config;
+use codec::{Encode, EncodeLike, Decode};
+use sp_core::H160;
+use sp_core::crypto::AccountId32;
+use core::cmp::Ordering;
+use serde::{Serialize, Deserialize};
+use pallet_evm::AddressMapping;
+use sp_std::vec::Vec;
+use sp_std::clone::Clone;
+
+pub trait CrossAccountId<AccountId>:
+ Encode + EncodeLike + Decode + Clone + PartialEq + Ord + core::fmt::Debug + Default
+// +
+// Serialize + Deserialize<'static>
+{
+ fn as_sub(&self) -> &AccountId;
+ fn as_eth(&self) -> &H160;
+
+ fn from_sub(account: AccountId) -> Self;
+ fn from_eth(account: H160) -> Self;
+}
+
+#[derive(Eq, Serialize, Deserialize)]
+pub struct BasicCrossAccountId<T: Config> {
+ /// If true - then ethereum is canonical encoding
+ from_ethereum: bool,
+ substrate: T::AccountId,
+ ethereum: H160,
+}
+
+impl<T: Config> Default for BasicCrossAccountId<T> {
+ fn default() -> Self {
+ Self::from_sub(T::AccountId::default())
+ }
+}
+
+impl<T: Config> core::fmt::Debug for BasicCrossAccountId<T> {
+ fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
+ if self.from_ethereum {
+ fmt.debug_tuple("CrossAccountId::Ethereum")
+ .field(&self.ethereum)
+ .finish()
+ } else {
+ fmt.debug_tuple("CrossAccountId::Substrate")
+ .field(&self.substrate)
+ .finish()
+ }
+ }
+}
+
+impl<T: Config> PartialOrd for BasicCrossAccountId<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
+ Some(self.substrate.cmp(&other.substrate))
+ }
+}
+
+impl<T: Config> Ord for BasicCrossAccountId<T> {
+ fn cmp(&self, other: &Self) -> Ordering {
+ self.partial_cmp(other)
+ .expect("substrate account is total ordered")
+ }
+}
+
+impl<T: Config> PartialEq for BasicCrossAccountId<T> {
+ fn eq(&self, other: &Self) -> bool {
+ if self.from_ethereum == other.from_ethereum {
+ self.substrate == other.substrate && self.ethereum == other.ethereum
+ } else if self.from_ethereum {
+ // ethereum is canonical encoding, but we need to compare derived address
+ self.substrate == other.substrate
+ } else {
+ self.ethereum == other.ethereum
+ }
+ }
+}
+impl<T: Config> Clone for BasicCrossAccountId<T> {
+ fn clone(&self) -> Self {
+ Self {
+ from_ethereum: self.from_ethereum,
+ substrate: self.substrate.clone(),
+ ethereum: self.ethereum,
+ }
+ }
+}
+impl<T: Config> Encode for BasicCrossAccountId<T> {
+ fn encode(&self) -> Vec<u8> {
+ let as_result = if !self.from_ethereum {
+ Ok(self.substrate.clone())
+ } else {
+ Err(self.ethereum)
+ };
+ as_result.encode()
+ }
+}
+impl<T: Config> EncodeLike for BasicCrossAccountId<T> {}
+impl<T: Config> Decode for BasicCrossAccountId<T> {
+ fn decode<I>(input: &mut I) -> Result<Self, codec::Error>
+ where
+ I: codec::Input,
+ {
+ Ok(match <Result<T::AccountId, H160>>::decode(input)? {
+ Ok(s) => Self::from_sub(s),
+ Err(e) => Self::from_eth(e),
+ })
+ }
+}
+impl<T: Config> CrossAccountId<T::AccountId> for BasicCrossAccountId<T> {
+ fn as_sub(&self) -> &T::AccountId {
+ &self.substrate
+ }
+ fn as_eth(&self) -> &H160 {
+ &self.ethereum
+ }
+ fn from_sub(substrate: T::AccountId) -> Self {
+ Self {
+ ethereum: T::EvmBackwardsAddressMapping::from_account_id(substrate.clone()),
+ substrate,
+ from_ethereum: false,
+ }
+ }
+ fn from_eth(ethereum: H160) -> Self {
+ Self {
+ ethereum,
+ substrate: T::EvmAddressMapping::into_account_id(ethereum),
+ from_ethereum: true,
+ }
+ }
+}
+
+pub trait EvmBackwardsAddressMapping<AccountId> {
+ fn from_account_id(account_id: AccountId) -> H160;
+}
+
+/// Should have same mapping as EnsureAddressTruncated
+pub struct MapBackwardsAddressTruncated;
+impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
+ fn from_account_id(account_id: AccountId32) -> H160 {
+ let mut out = [0; 20];
+ out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
+ H160(out)
+ }
+}
pallets/common/src/benchmarking.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/benchmarking.rs
@@ -0,0 +1 @@
+#![cfg(feature = "runtime-benchmarking")]
pallets/common/src/erc.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/erc.rs
@@ -0,0 +1,10 @@
+pub use pallet_evm::PrecompileOutput;
+use sp_core::{H160, U256};
+
+/// Does not always represent a full collection, for RFT it is either
+/// collection (Implementing ERC721), or specific collection token (Implementing ERC20)
+pub trait CommonEvmHandler {
+ const CODE: &'static [u8];
+
+ fn call(self, source: &H160, input: &[u8], value: U256) -> Option<PrecompileOutput>;
+}
pallets/common/src/eth.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/eth.rs
@@ -0,0 +1,50 @@
+use nft_data_structs::{CollectionId, TokenId};
+use sp_core::H160;
+
+// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection 1
+// TODO: Unhardcode prefix
+const ETH_ACCOUNT_PREFIX: [u8; 16] = [
+ 0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,
+];
+
+// 0xf8238ccfff8ed887463fd5e00000000100000002 - collection 1, token 2
+// TODO: Unhardcode prefix
+const ETH_ACCOUNT_TOKEN_PREFIX: [u8; 12] = [
+ 0xf8, 0x23, 0x8c, 0xcf, 0xff, 0x8e, 0xd8, 0x87, 0x46, 0x3f, 0xd5, 0xe0,
+];
+
+pub fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {
+ if eth[0..16] != ETH_ACCOUNT_PREFIX {
+ return None;
+ }
+ let mut id_bytes = [0; 4];
+ id_bytes.copy_from_slice(ð[16..20]);
+ Some(CollectionId(u32::from_be_bytes(id_bytes)))
+}
+pub fn collection_id_to_address(id: CollectionId) -> H160 {
+ let mut out = [0; 20];
+ out[0..16].copy_from_slice(Ð_ACCOUNT_PREFIX);
+ out[16..20].copy_from_slice(&u32::to_be_bytes(id.0));
+ H160(out)
+}
+
+pub fn map_eth_to_token_id(eth: &H160) -> Option<(CollectionId, TokenId)> {
+ if eth[0..12] != ETH_ACCOUNT_TOKEN_PREFIX {
+ return None;
+ }
+ let mut id_bytes = [0; 4];
+ let mut token_id_bytes = [0; 4];
+ id_bytes.copy_from_slice(ð[12..16]);
+ token_id_bytes.copy_from_slice(ð[16..20]);
+ Some((
+ CollectionId(u32::from_be_bytes(id_bytes)),
+ TokenId(u32::from_be_bytes(token_id_bytes)),
+ ))
+}
+pub fn collection_token_id_to_address(id: CollectionId, token: TokenId) -> H160 {
+ let mut out = [0; 20];
+ out[0..12].copy_from_slice(Ð_ACCOUNT_TOKEN_PREFIX);
+ out[12..16].copy_from_slice(&u32::to_be_bytes(id.0));
+ out[16..20].copy_from_slice(&u32::to_be_bytes(token.0));
+ H160(out)
+}
pallets/common/src/lib.rsdiffbeforeafterboth--- /dev/null
+++ b/pallets/common/src/lib.rs
@@ -0,0 +1,543 @@
+#![cfg_attr(not(feature = "std"), no_std)]
+
+use core::ops::{Deref, DerefMut};
+use sp_std::vec::Vec;
+use account::CrossAccountId;
+use frame_support::{
+ dispatch::{DispatchErrorWithPostInfo, DispatchResultWithPostInfo},
+ ensure, fail,
+ traits::{Imbalance, Get, Currency},
+};
+use nft_data_structs::{
+ COLLECTION_NUMBER_LIMIT, Collection, CollectionId, CreateItemData, ExistenceRequirement,
+ MAX_COLLECTION_DESCRIPTION_LENGTH, MAX_COLLECTION_NAME_LENGTH, MAX_TOKEN_PREFIX_LENGTH,
+ MetaUpdatePermission, Pays, PostDispatchInfo, TokenId, Weight, WithdrawReasons,
+};
+pub use pallet::*;
+use sp_core::H160;
+use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
+pub mod account;
+pub mod benchmarking;
+pub mod erc;
+pub mod eth;
+
+#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]
+pub struct CollectionHandle<T: Config> {
+ pub id: CollectionId,
+ collection: Collection<T>,
+ pub recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
+}
+impl<T: Config> CollectionHandle<T> {
+ pub fn new_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
+ <CollectionById<T>>::get(id).map(|collection| Self {
+ id,
+ collection,
+ recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(
+ eth::collection_id_to_address(id),
+ gas_limit,
+ ),
+ })
+ }
+ pub fn new(id: CollectionId) -> Option<Self> {
+ Self::new_with_gas_limit(id, u64::MAX)
+ }
+ pub fn try_get(id: CollectionId) -> Result<Self, DispatchError> {
+ Ok(Self::new(id).ok_or_else(|| <Error<T>>::CollectionNotFound)?)
+ }
+ pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {
+ self.recorder.log_sub(log)
+ }
+ pub fn log_infallible(&self, log: impl evm_coder::ToLog) {
+ self.recorder.log_infallible(log)
+ }
+ #[allow(dead_code)]
+ fn consume_gas(&self, gas: u64) -> DispatchResult {
+ self.recorder.consume_gas_sub(gas)
+ }
+ pub fn consume_sload(&self) -> DispatchResult {
+ self.recorder.consume_sload_sub()
+ }
+ pub fn consume_sstores(&self, amount: usize) -> DispatchResult {
+ self.recorder.consume_sstores_sub(amount)
+ }
+ pub fn consume_sstore(&self) -> DispatchResult {
+ self.recorder.consume_sstore_sub()
+ }
+ pub fn consume_log(&self, topics: usize, data: usize) -> DispatchResult {
+ self.recorder.consume_log_sub(topics, data)
+ }
+ pub fn submit_logs(self) -> DispatchResult {
+ self.recorder.submit_logs()
+ }
+ pub fn save(self) -> DispatchResult {
+ self.recorder.submit_logs()?;
+ <CollectionById<T>>::insert(self.id, self.collection);
+ Ok(())
+ }
+}
+impl<T: Config> Deref for CollectionHandle<T> {
+ type Target = Collection<T>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.collection
+ }
+}
+
+impl<T: Config> DerefMut for CollectionHandle<T> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.collection
+ }
+}
+
+impl<T: Config> CollectionHandle<T> {
+ pub fn check_is_owner(&self, subject: &T::CrossAccountId) -> DispatchResult {
+ ensure!(*subject.as_sub() == self.owner, <Error<T>>::NoPermission);
+ Ok(())
+ }
+ pub fn is_owner_or_admin(&self, subject: &T::CrossAccountId) -> Result<bool, DispatchError> {
+ self.consume_sload()?;
+
+ Ok(*subject.as_sub() == self.owner || <IsAdmin<T>>::get((self.id, subject.as_sub())))
+ }
+ pub fn check_is_owner_or_admin(&self, subject: &T::CrossAccountId) -> DispatchResult {
+ ensure!(self.is_owner_or_admin(subject)?, <Error<T>>::NoPermission);
+ Ok(())
+ }
+ pub fn ignores_allowance(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
+ Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)
+ }
+ pub fn ignores_owned_amount(&self, user: &T::CrossAccountId) -> Result<bool, DispatchError> {
+ Ok(self.limits.owner_can_transfer && self.is_owner_or_admin(user)?)
+ }
+ pub fn check_whitelist(&self, user: &T::CrossAccountId) -> DispatchResult {
+ self.consume_sload()?;
+
+ ensure!(
+ <WhiteList<T>>::get((self.id, user.as_sub())),
+ <Error<T>>::AddressNotInWhiteList
+ );
+ Ok(())
+ }
+
+ pub fn check_can_update_meta(
+ &self,
+ subject: &T::CrossAccountId,
+ item_owner: &T::CrossAccountId,
+ ) -> DispatchResult {
+ match self.meta_update_permission {
+ MetaUpdatePermission::ItemOwner => {
+ ensure!(subject == item_owner, <Error<T>>::NoPermission);
+ Ok(())
+ }
+ MetaUpdatePermission::Admin => self.check_is_owner_or_admin(subject),
+ MetaUpdatePermission::None => fail!(<Error<T>>::NoPermission),
+ }
+ }
+}
+
+#[frame_support::pallet]
+pub mod pallet {
+ use super::*;
+ use frame_support::{pallet_prelude::*};
+ use frame_support::{Blake2_128Concat, storage::Key};
+ use account::{EvmBackwardsAddressMapping, CrossAccountId};
+ use frame_support::traits::Currency;
+ use nft_data_structs::TokenId;
+
+ #[pallet::config]
+ pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config {
+ type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
+
+ type CrossAccountId: CrossAccountId<Self::AccountId>;
+
+ type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;
+ type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;
+
+ type Currency: Currency<Self::AccountId>;
+ type CollectionCreationPrice: Get<
+ <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,
+ >;
+ type TreasuryAccountId: Get<Self::AccountId>;
+ }
+
+ #[pallet::pallet]
+ #[pallet::generate_store(pub(super) trait Store)]
+ pub struct Pallet<T>(_);
+
+ #[pallet::event]
+ #[pallet::generate_deposit(pub fn deposit_event)]
+ pub enum Event<T: Config> {
+ /// New collection was created
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: Globally unique identifier of newly created collection.
+ ///
+ /// * mode: [CollectionMode] converted into u8.
+ ///
+ /// * account_id: Collection owner.
+ CollectionCreated(CollectionId, u8, T::AccountId),
+
+ /// New item was created.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id: Id of the collection where item was created.
+ ///
+ /// * item_id: Id of an item. Unique within the collection.
+ ///
+ /// * recipient: Owner of newly created item
+ ///
+ /// * amount: Always 1 for NFT
+ ItemCreated(CollectionId, TokenId, T::CrossAccountId, u128),
+
+ /// Collection item was burned.
+ ///
+ /// # Arguments
+ ///
+ /// * collection_id.
+ ///
+ /// * item_id: Identifier of burned NFT.
+ ///
+ /// * owner: which user has destroyed its tokens
+ ///
+ /// * amount: Always 1 for NFT
+ ItemDestroyed(CollectionId, TokenId, T::CrossAccountId, u128),
+
+ /// Item was transferred
+ ///
+ /// * collection_id: Id of collection to which item is belong
+ ///
+ /// * item_id: Id of an item
+ ///
+ /// * sender: Original owner of item
+ ///
+ /// * recipient: New owner of item
+ ///
+ /// * amount: Always 1 for NFT
+ Transfer(
+ CollectionId,
+ TokenId,
+ T::CrossAccountId,
+ T::CrossAccountId,
+ u128,
+ ),
+
+ /// * collection_id
+ ///
+ /// * item_id
+ ///
+ /// * sender
+ ///
+ /// * spender
+ ///
+ /// * amount
+ Approved(
+ CollectionId,
+ TokenId,
+ T::CrossAccountId,
+ T::CrossAccountId,
+ u128,
+ ),
+ }
+
+ #[pallet::error]
+ pub enum Error<T> {
+ /// This collection does not exist.
+ CollectionNotFound,
+ /// Sender parameter and item owner must be equal.
+ MustBeTokenOwner,
+ /// No permission to perform action
+ NoPermission,
+ /// Collection is not in mint mode.
+ PublicMintingNotAllowed,
+ /// Address is not in white list.
+ AddressNotInWhiteList,
+
+ /// Collection name can not be longer than 63 char.
+ CollectionNameLimitExceeded,
+ /// Collection description can not be longer than 255 char.
+ CollectionDescriptionLimitExceeded,
+ /// Token prefix can not be longer than 15 char.
+ CollectionTokenPrefixLimitExceeded,
+ /// Total collections bound exceeded.
+ TotalCollectionsLimitExceeded,
+ /// variable_data exceeded data limit.
+ TokenVariableDataLimitExceeded,
+
+ /// Collection settings not allowing items transferring
+ TransferNotAllowed,
+ /// Account token limit exceeded per collection
+ AccountTokenLimitExceeded,
+ /// Collection token limit exceeded
+ CollectionTokenLimitExceeded,
+ /// Metadata flag frozen
+ MetadataFlagFrozen,
+
+ /// Item not exists.
+ TokenNotFound,
+ /// Item balance not enough.
+ TokenValueTooLow,
+ /// Requested value more than approved.
+ TokenValueNotEnough,
+ /// Tried to approve more than owned
+ CantApproveMoreThanOwned,
+
+ /// Can't transfer tokens to ethereum zero address
+ AddressIsZero,
+ /// Target collection doesn't supports this operation
+ UnsupportedOperation,
+ }
+
+ #[pallet::storage]
+ pub type CreatedCollectionCount<T> = StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+ #[pallet::storage]
+ pub type DestroyedCollectionCount<T> =
+ StorageValue<Value = CollectionId, QueryKind = ValueQuery>;
+
+ /// Collection info
+ #[pallet::storage]
+ pub type CollectionById<T> = StorageMap<
+ Hasher = Blake2_128Concat,
+ Key = CollectionId,
+ Value = Collection<T>,
+ QueryKind = OptionQuery,
+ >;
+
+ /// List of collection admins
+ #[pallet::storage]
+ pub type IsAdmin<T: Config> = StorageNMap<
+ Key = (
+ Key<Blake2_128Concat, CollectionId>,
+ Key<Blake2_128Concat, T::AccountId>,
+ ),
+ Value = bool,
+ QueryKind = ValueQuery,
+ >;
+
+ /// Whitelisted collection users
+ #[pallet::storage]
+ pub type WhiteList<T: Config> = StorageNMap<
+ Key = (
+ Key<Blake2_128Concat, CollectionId>,
+ Key<Blake2_128Concat, T::AccountId>,
+ ),
+ Value = bool,
+ QueryKind = ValueQuery,
+ >;
+}
+
+impl<T: Config> Pallet<T> {
+ /// Ethereum receiver 0x0000000000000000000000000000000000000000 is reserved, and shouldn't own tokens
+ pub fn ensure_correct_receiver(receiver: &T::CrossAccountId) -> DispatchResult {
+ ensure!(
+ &T::CrossAccountId::from_eth(H160([0; 20])) != receiver,
+ <Error<T>>::AddressIsZero
+ );
+ Ok(())
+ }
+}
+
+impl<T: Config> Pallet<T> {
+ pub fn init_collection(data: Collection<T>) -> Result<CollectionId, DispatchError> {
+ {
+ ensure!(
+ data.name.len() <= MAX_COLLECTION_NAME_LENGTH,
+ Error::<T>::CollectionNameLimitExceeded
+ );
+ ensure!(
+ data.description.len() <= MAX_COLLECTION_DESCRIPTION_LENGTH,
+ Error::<T>::CollectionDescriptionLimitExceeded
+ );
+ ensure!(
+ data.token_prefix.len() <= MAX_TOKEN_PREFIX_LENGTH,
+ Error::<T>::CollectionTokenPrefixLimitExceeded
+ );
+ }
+
+ let created_count = <CreatedCollectionCount<T>>::get()
+ .0
+ .checked_add(1)
+ .ok_or(ArithmeticError::Overflow)?;
+ let destroyed_count = <DestroyedCollectionCount<T>>::get().0;
+ let id = CollectionId(created_count);
+
+ // bound Total number of collections
+ ensure!(
+ created_count - destroyed_count < COLLECTION_NUMBER_LIMIT,
+ <Error<T>>::TotalCollectionsLimitExceeded
+ );
+
+ // =========
+
+ // Take a (non-refundable) deposit of collection creation
+ {
+ let mut imbalance =
+ <<<T as Config>::Currency as Currency<T::AccountId>>::PositiveImbalance>::zero();
+ imbalance.subsume(
+ <<T as Config>::Currency as Currency<T::AccountId>>::deposit_creating(
+ &T::TreasuryAccountId::get(),
+ T::CollectionCreationPrice::get(),
+ ),
+ );
+ <T as Config>::Currency::settle(
+ &data.owner,
+ imbalance,
+ WithdrawReasons::TRANSFER,
+ ExistenceRequirement::KeepAlive,
+ )
+ .map_err(|_| Error::<T>::NoPermission)?;
+ }
+
+ <CreatedCollectionCount<T>>::put(created_count);
+ <Pallet<T>>::deposit_event(Event::CollectionCreated(
+ id,
+ data.mode.id(),
+ data.owner.clone(),
+ ));
+ <CollectionById<T>>::insert(id, data);
+ Ok(id)
+ }
+ pub fn destroy_collection(
+ collection: CollectionHandle<T>,
+ sender: &T::CrossAccountId,
+ ) -> DispatchResult {
+ if !collection.limits.owner_can_destroy {
+ fail!(Error::<T>::NoPermission);
+ }
+ collection.check_is_owner(&sender)?;
+
+ let destroyed_collections = <DestroyedCollectionCount<T>>::get()
+ .0
+ .checked_add(1)
+ .ok_or(ArithmeticError::Overflow)?;
+
+ // =========
+
+ <DestroyedCollectionCount<T>>::put(destroyed_collections);
+ <CollectionById<T>>::remove(collection.id);
+ <IsAdmin<T>>::remove_prefix((collection.id,), None);
+ <WhiteList<T>>::remove_prefix((collection.id,), None);
+ Ok(())
+ }
+
+ pub fn toggle_whitelist(
+ collection: &CollectionHandle<T>,
+ sender: &T::CrossAccountId,
+ user: &T::CrossAccountId,
+ allowed: bool,
+ ) -> DispatchResult {
+ collection.check_is_owner_or_admin(&sender)?;
+
+ // =========
+
+ if allowed {
+ <WhiteList<T>>::insert((collection.id, user.as_sub()), true);
+ } else {
+ <WhiteList<T>>::remove((collection.id, user.as_sub()));
+ }
+
+ Ok(())
+ }
+}
+
+#[macro_export]
+macro_rules! unsupported {
+ () => {
+ Err(<Error<T>>::UnsupportedOperation.into())
+ };
+}
+
+/// Worst cases
+pub trait CommonWeightInfo {
+ fn create_item() -> Weight;
+ fn create_multiple_items(amount: u32) -> Weight;
+ fn burn_item() -> Weight;
+ fn transfer() -> Weight;
+ fn approve() -> Weight;
+ fn transfer_from() -> Weight;
+ fn set_variable_metadata(bytes: u32) -> Weight;
+}
+
+pub trait CommonCollectionOperations<T: Config> {
+ fn create_item(
+ &self,
+ sender: T::CrossAccountId,
+ to: T::CrossAccountId,
+ data: CreateItemData,
+ ) -> DispatchResultWithPostInfo;
+ fn create_multiple_items(
+ &self,
+ sender: T::CrossAccountId,
+ to: T::CrossAccountId,
+ data: Vec<CreateItemData>,
+ ) -> DispatchResultWithPostInfo;
+ fn burn_item(
+ &self,
+ sender: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> DispatchResultWithPostInfo;
+
+ fn transfer(
+ &self,
+ sender: T::CrossAccountId,
+ to: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> DispatchResultWithPostInfo;
+ fn approve(
+ &self,
+ sender: T::CrossAccountId,
+ spender: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> DispatchResultWithPostInfo;
+ fn transfer_from(
+ &self,
+ sender: T::CrossAccountId,
+ from: T::CrossAccountId,
+ to: T::CrossAccountId,
+ token: TokenId,
+ amount: u128,
+ ) -> DispatchResultWithPostInfo;
+
+ fn set_variable_metadata(
+ &self,
+ sender: T::CrossAccountId,
+ token: TokenId,
+ data: Vec<u8>,
+ ) -> DispatchResultWithPostInfo;
+
+ fn account_tokens(&self, account: T::CrossAccountId) -> Vec<TokenId>;
+ fn token_exists(&self, token: TokenId) -> bool;
+
+ fn token_owner(&self, token: TokenId) -> T::CrossAccountId;
+ fn const_metadata(&self, token: TokenId) -> Vec<u8>;
+ fn variable_metadata(&self, token: TokenId) -> Vec<u8>;
+
+ /// How many tokens collection contains (Applicable to nonfungible/refungible)
+ fn collection_tokens(&self) -> u32;
+ /// Amount of different tokens account has (Applicable to nonfungible/refungible)
+ fn account_balance(&self, account: T::CrossAccountId) -> u32;
+ /// Amount of specific token account have (Applicable to fungible/refungible)
+ fn balance(&self, account: T::CrossAccountId, token: TokenId) -> u128;
+ fn allowance(
+ &self,
+ sender: T::CrossAccountId,
+ spender: T::CrossAccountId,
+ token: TokenId,
+ ) -> u128;
+}
+
+// Flexible enough for implementing CommonCollectionOperations
+pub fn with_weight(res: DispatchResult, weight: Weight) -> DispatchResultWithPostInfo {
+ let post_info = PostDispatchInfo {
+ actual_weight: Some(weight),
+ pays_fee: Pays::Yes,
+ };
+ match res {
+ Ok(()) => Ok(post_info),
+ Err(error) => Err(DispatchErrorWithPostInfo { post_info, error }),
+ }
+}
pallets/nft/src/eth/account.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/account.rs
+++ /dev/null
@@ -1,136 +0,0 @@
-use crate::Config;
-use codec::{Encode, EncodeLike, Decode};
-use sp_core::crypto::AccountId32;
-use primitive_types::H160;
-use core::cmp::Ordering;
-use serde::{Serialize, Deserialize};
-use pallet_evm::AddressMapping;
-use sp_std::vec::Vec;
-use sp_std::clone::Clone;
-
-pub trait CrossAccountId<AccountId>:
- Encode + EncodeLike + Decode + Clone + PartialEq + Ord + core::fmt::Debug
-// +
-// Serialize + Deserialize<'static>
-{
- fn as_sub(&self) -> &AccountId;
- fn as_eth(&self) -> &H160;
-
- fn from_sub(account: AccountId) -> Self;
- fn from_eth(account: H160) -> Self;
-}
-
-#[derive(Eq, Serialize, Deserialize)]
-pub struct BasicCrossAccountId<T: Config> {
- /// If true - then ethereum is canonical encoding
- from_ethereum: bool,
- substrate: T::AccountId,
- ethereum: H160,
-}
-
-impl<T: Config> core::fmt::Debug for BasicCrossAccountId<T> {
- fn fmt(&self, fmt: &mut core::fmt::Formatter) -> core::fmt::Result {
- if self.from_ethereum {
- fmt.debug_tuple("CrossAccountId::Ethereum")
- .field(&self.ethereum)
- .finish()
- } else {
- fmt.debug_tuple("CrossAccountId::Substrate")
- .field(&self.substrate)
- .finish()
- }
- }
-}
-
-impl<T: Config> PartialOrd for BasicCrossAccountId<T> {
- fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
- Some(self.substrate.cmp(&other.substrate))
- }
-}
-
-impl<T: Config> Ord for BasicCrossAccountId<T> {
- fn cmp(&self, other: &Self) -> Ordering {
- self.partial_cmp(other)
- .expect("substrate account is total ordered")
- }
-}
-
-impl<T: Config> PartialEq for BasicCrossAccountId<T> {
- fn eq(&self, other: &Self) -> bool {
- if self.from_ethereum == other.from_ethereum {
- self.substrate == other.substrate && self.ethereum == other.ethereum
- } else if self.from_ethereum {
- // ethereum is canonical encoding, but we need to compare derived address
- self.substrate == other.substrate
- } else {
- self.ethereum == other.ethereum
- }
- }
-}
-impl<T: Config> Clone for BasicCrossAccountId<T> {
- fn clone(&self) -> Self {
- Self {
- from_ethereum: self.from_ethereum,
- substrate: self.substrate.clone(),
- ethereum: self.ethereum,
- }
- }
-}
-impl<T: Config> Encode for BasicCrossAccountId<T> {
- fn encode(&self) -> Vec<u8> {
- let as_result = if !self.from_ethereum {
- Ok(self.substrate.clone())
- } else {
- Err(self.ethereum)
- };
- as_result.encode()
- }
-}
-impl<T: Config> EncodeLike for BasicCrossAccountId<T> {}
-impl<T: Config> Decode for BasicCrossAccountId<T> {
- fn decode<I>(input: &mut I) -> Result<Self, codec::Error>
- where
- I: codec::Input,
- {
- Ok(match <Result<T::AccountId, H160>>::decode(input)? {
- Ok(s) => Self::from_sub(s),
- Err(e) => Self::from_eth(e),
- })
- }
-}
-impl<T: Config> CrossAccountId<T::AccountId> for BasicCrossAccountId<T> {
- fn as_sub(&self) -> &T::AccountId {
- &self.substrate
- }
- fn as_eth(&self) -> &H160 {
- &self.ethereum
- }
- fn from_sub(substrate: T::AccountId) -> Self {
- Self {
- ethereum: T::EvmBackwardsAddressMapping::from_account_id(substrate.clone()),
- substrate,
- from_ethereum: false,
- }
- }
- fn from_eth(ethereum: H160) -> Self {
- Self {
- ethereum,
- substrate: T::EvmAddressMapping::into_account_id(ethereum),
- from_ethereum: true,
- }
- }
-}
-
-pub trait EvmBackwardsAddressMapping<AccountId> {
- fn from_account_id(account_id: AccountId) -> H160;
-}
-
-/// Should have same mapping as EnsureAddressTruncated
-pub struct MapBackwardsAddressTruncated;
-impl EvmBackwardsAddressMapping<AccountId32> for MapBackwardsAddressTruncated {
- fn from_account_id(account_id: AccountId32) -> H160 {
- let mut out = [0; 20];
- out.copy_from_slice(&(account_id.as_ref() as &[u8])[0..20]);
- H160(out)
- }
-}
pallets/nft/src/eth/erc.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/erc.rs
+++ /dev/null
@@ -1,496 +0,0 @@
-use core::char::{decode_utf16, REPLACEMENT_CHARACTER};
-use evm_coder::{ToLog, execution::Result, generate_stubgen, solidity, solidity_interface, types::*};
-use nft_data_structs::{CreateItemData, CreateNftData};
-use core::convert::TryInto;
-use crate::{
- Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList,
- ItemListIndex,
-};
-use frame_support::storage::{StorageMap, StorageDoubleMap};
-use pallet_evm::AddressMapping;
-use pallet_evm_coder_substrate::dispatch_to_evm;
-use super::account::CrossAccountId;
-use sp_std::{vec, vec::Vec};
-
-#[solidity_interface(name = "ERC165")]
-impl<T: Config> CollectionHandle<T> {
- fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {
- Ok(match self.mode {
- CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),
- CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),
- _ => false,
- })
- }
-}
-
-#[solidity_interface(name = "InlineNameSymbol")]
-impl<T: Config> CollectionHandle<T> {
- fn name(&self) -> Result<string> {
- Ok(decode_utf16(self.name.iter().copied())
- .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
- .collect::<string>())
- }
-
- fn symbol(&self) -> Result<string> {
- Ok(string::from_utf8_lossy(&self.token_prefix).into())
- }
-}
-
-#[solidity_interface(name = "InlineTotalSupply")]
-impl<T: Config> CollectionHandle<T> {
- fn total_supply(&self) -> Result<uint256> {
- // TODO: we do not track total amount of all tokens
- Ok(0.into())
- }
-}
-
-#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]
-impl<T: Config> CollectionHandle<T> {
- #[solidity(rename_selector = "tokenURI")]
- fn token_uri(&self, token_id: uint256) -> Result<string> {
- let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
- Ok(string::from_utf8_lossy(
- &<NftItemList<T>>::get(self.id, token_id)
- .ok_or("token not found")?
- .const_data,
- )
- .into())
- }
-}
-
-#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]
-impl<T: Config> CollectionHandle<T> {
- fn token_by_index(&self, index: uint256) -> Result<uint256> {
- Ok(index)
- }
-
- fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
- // TODO: Not implemetable
- Err("not implemented".into())
- }
-}
-
-#[derive(ToLog)]
-pub enum ERC721Events {
- Transfer {
- #[indexed]
- from: address,
- #[indexed]
- to: address,
- #[indexed]
- token_id: uint256,
- },
- Approval {
- #[indexed]
- owner: address,
- #[indexed]
- approved: address,
- #[indexed]
- token_id: uint256,
- },
- #[allow(dead_code)]
- ApprovalForAll {
- #[indexed]
- owner: address,
- #[indexed]
- operator: address,
- approved: bool,
- },
-}
-
-#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]
-impl<T: Config> CollectionHandle<T> {
- #[solidity(rename_selector = "balanceOf")]
- fn balance_of_nft(&self, owner: address) -> Result<uint256> {
- let owner = T::EvmAddressMapping::into_account_id(owner);
- let balance = <Balance<T>>::get(self.id, owner);
- Ok(balance.into())
- }
- fn owner_of(&self, token_id: uint256) -> Result<address> {
- let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
- let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;
- Ok(*token.owner.as_eth())
- }
- fn safe_transfer_from_with_data(
- &mut self,
- _from: address,
- _to: address,
- _token_id: uint256,
- _data: bytes,
- _value: value,
- ) -> Result<void> {
- // TODO: Not implemetable
- Err("not implemented".into())
- }
- fn safe_transfer_from(
- &mut self,
- _from: address,
- _to: address,
- _token_id: uint256,
- _value: value,
- ) -> Result<void> {
- // TODO: Not implemetable
- Err("not implemented".into())
- }
-
- fn transfer_from(
- &mut self,
- caller: caller,
- from: address,
- to: address,
- token_id: uint256,
- _value: value,
- ) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- let from = T::CrossAccountId::from_eth(from);
- let to = T::CrossAccountId::from_eth(to);
- let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
-
- <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)
- .map_err(|_| "transferFrom error")?;
- Ok(())
- }
-
- fn approve(
- &mut self,
- caller: caller,
- approved: address,
- token_id: uint256,
- _value: value,
- ) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- let approved = T::CrossAccountId::from_eth(approved);
- let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
-
- <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)
- .map_err(|_| "approve internal")?;
- Ok(())
- }
-
- fn set_approval_for_all(
- &mut self,
- _caller: caller,
- _operator: address,
- _approved: bool,
- ) -> Result<void> {
- // TODO: Not implemetable
- Err("not implemented".into())
- }
-
- fn get_approved(&self, _token_id: uint256) -> Result<address> {
- // TODO: Not implemetable
- Err("not implemented".into())
- }
-
- fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
- // TODO: Not implemetable
- Err("not implemented".into())
- }
-}
-
-#[solidity_interface(name = "ERC721Burnable")]
-impl<T: Config> CollectionHandle<T> {
- fn burn(&mut self, caller: caller, token_id: uint256) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
-
- <Module<T>>::burn_item_internal(&caller, &self, token_id, 1).map_err(|_| "burn error")?;
- Ok(())
- }
-}
-
-#[derive(ToLog)]
-pub enum ERC721MintableEvents {
- #[allow(dead_code)]
- MintingFinished {},
-}
-
-#[solidity_interface(name = "ERC721Mintable", events(ERC721MintableEvents))]
-impl<T: Config> CollectionHandle<T> {
- fn minting_finished(&self) -> Result<bool> {
- Ok(false)
- }
-
- fn mint(&mut self, caller: caller, to: address, token_id: uint256) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
- if <ItemListIndex>::get(self.id)
- .checked_add(1)
- .ok_or("item id overflow")?
- != token_id
- {
- return Err("item id should be next".into());
- }
-
- <Module<T>>::create_item_internal(
- &caller,
- &self,
- &to,
- CreateItemData::NFT(CreateNftData {
- const_data: vec![].try_into().unwrap(),
- variable_data: vec![].try_into().unwrap(),
- }),
- )
- .map_err(|_| "mint error")?;
- Ok(true)
- }
-
- #[solidity(rename_selector = "mintWithTokenURI")]
- fn mint_with_token_uri(
- &mut self,
- caller: caller,
- to: address,
- token_id: uint256,
- token_uri: string,
- ) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let token_id: u32 = token_id.try_into().map_err(|_| "amount overflow")?;
- if <ItemListIndex>::get(self.id)
- .checked_add(1)
- .ok_or("item id overflow")?
- != token_id
- {
- return Err("item id should be next".into());
- }
-
- <Module<T>>::create_item_internal(
- &caller,
- &self,
- &to,
- CreateItemData::NFT(CreateNftData {
- const_data: Vec::<u8>::from(token_uri)
- .try_into()
- .map_err(|_| "token uri is too long")?,
- variable_data: vec![].try_into().unwrap(),
- }),
- )
- .map_err(|_| "mint error")?;
- Ok(true)
- }
-
- fn finish_minting(&mut self, _caller: caller) -> Result<bool> {
- Err("not implementable".into())
- }
-}
-
-#[solidity_interface(name = "ERC721UniqueExtensions")]
-impl<T: Config> CollectionHandle<T> {
- #[solidity(rename_selector = "transfer")]
- fn transfer_nft(
- &mut self,
- caller: caller,
- to: address,
- token_id: uint256,
- _value: value,
- ) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
-
- <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)
- .map_err(|_| "transfer error")?;
- Ok(())
- }
-
- fn next_token_id(&self) -> Result<uint256> {
- Ok(ItemListIndex::get(self.id)
- .checked_add(1)
- .ok_or("item id overflow")?
- .into())
- }
-
- fn set_variable_metadata(
- &mut self,
- caller: caller,
- token_id: uint256,
- data: bytes,
- ) -> Result<void> {
- let caller = T::CrossAccountId::from_eth(caller);
- let token_id = token_id.try_into().map_err(|_| "token id overflow")?;
-
- <Module<T>>::set_variable_meta_data_internal(&caller, self, token_id, data)
- .map_err(dispatch_to_evm::<T>)?;
- Ok(())
- }
-
- fn get_variable_metadata(&self, token_id: uint256) -> Result<bytes> {
- let token_id = token_id.try_into().map_err(|_| "token id overflow")?;
-
- <Module<T>>::get_variable_metadata(self, token_id).map_err(dispatch_to_evm::<T>)
- }
-
- fn mint_bulk(&mut self, caller: caller, to: address, token_ids: Vec<uint256>) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let mut expected_index = <ItemListIndex>::get(self.id)
- .checked_add(1)
- .ok_or("item id overflow")?;
-
- let total_tokens = token_ids.len();
- for id in token_ids.into_iter() {
- let id: u32 = id.try_into().map_err(|_| "token id overflow")?;
- if id != expected_index {
- return Err("item id should be next".into());
- }
- expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
- }
-
- let data = (0..total_tokens)
- .map(|_| {
- CreateItemData::NFT(CreateNftData {
- const_data: vec![].try_into().unwrap(),
- variable_data: vec![].try_into().unwrap(),
- })
- })
- .collect();
-
- <Module<T>>::create_multiple_items_internal(&caller, self, &to, data)
- .map_err(dispatch_to_evm::<T>)?;
- Ok(true)
- }
-
- #[solidity(rename_selector = "mintBulkWithTokenURI")]
- fn mint_bulk_with_token_uri(
- &mut self,
- caller: caller,
- to: address,
- tokens: Vec<(uint256, string)>,
- ) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let mut expected_index = <ItemListIndex>::get(self.id)
- .checked_add(1)
- .ok_or("item id overflow")?;
-
- let mut data = Vec::with_capacity(tokens.len());
- for (id, token_uri) in tokens {
- let id: u32 = id.try_into().map_err(|_| "token id overflow")?;
- if id != expected_index {
- panic!("item id should be next ({}) but got {}", expected_index, id);
- }
- expected_index = expected_index.checked_add(1).ok_or("item id overflow")?;
-
- data.push(CreateItemData::NFT(CreateNftData {
- const_data: Vec::<u8>::from(token_uri)
- .try_into()
- .map_err(|_| "token uri is too long")?,
- variable_data: vec![].try_into().unwrap(),
- }));
- }
-
- <Module<T>>::create_multiple_items_internal(&caller, self, &to, data)
- .map_err(dispatch_to_evm::<T>)?;
- Ok(true)
- }
-}
-
-#[solidity_interface(
- name = "UniqueNFT",
- is(
- ERC165,
- ERC721,
- ERC721Metadata,
- ERC721Enumerable,
- ERC721UniqueExtensions,
- ERC721Mintable,
- ERC721Burnable,
- )
-)]
-impl<T: Config> CollectionHandle<T> {}
-
-#[derive(ToLog)]
-pub enum ERC20Events {
- Transfer {
- #[indexed]
- from: address,
- #[indexed]
- to: address,
- value: uint256,
- },
- Approval {
- #[indexed]
- owner: address,
- #[indexed]
- spender: address,
- value: uint256,
- },
-}
-
-#[solidity_interface(
- name = "ERC20",
- inline_is(InlineNameSymbol, InlineTotalSupply),
- events(ERC20Events)
-)]
-impl<T: Config> CollectionHandle<T> {
- fn decimals(&self) -> Result<uint8> {
- Ok(if let CollectionMode::Fungible(decimals) = &self.mode {
- *decimals
- } else {
- unreachable!()
- })
- }
- fn balance_of(&self, owner: address) -> Result<uint256> {
- let owner = T::EvmAddressMapping::into_account_id(owner);
- let balance = <Balance<T>>::get(self.id, owner);
- Ok(balance.into())
- }
- fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let to = T::CrossAccountId::from_eth(to);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
- <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)
- .map_err(|_| "transfer error")?;
- Ok(true)
- }
- #[solidity(rename_selector = "transferFrom")]
- fn transfer_from_fungible(
- &mut self,
- caller: caller,
- from: address,
- to: address,
- amount: uint256,
- ) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let from = T::CrossAccountId::from_eth(from);
- let to = T::CrossAccountId::from_eth(to);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
- <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)
- .map_err(|_| "transferFrom error")?;
- Ok(true)
- }
- #[solidity(rename_selector = "approve")]
- fn approve_fungible(
- &mut self,
- caller: caller,
- spender: address,
- amount: uint256,
- ) -> Result<bool> {
- let caller = T::CrossAccountId::from_eth(caller);
- let spender = T::CrossAccountId::from_eth(spender);
- let amount = amount.try_into().map_err(|_| "amount overflow")?;
-
- <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)
- .map_err(|_| "approve internal")?;
- Ok(true)
- }
- fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
- let owner = T::CrossAccountId::from_eth(owner);
- let spender = T::CrossAccountId::from_eth(spender);
-
- Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())
- }
-}
-
-#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
-impl<T: Config> CollectionHandle<T> {}
-
-// Not a tests, but code generators
-generate_stubgen!(nft_impl, UniqueNFTCall, true);
-generate_stubgen!(nft_iface, UniqueNFTCall, false);
-
-generate_stubgen!(fungible_impl, UniqueFungibleCall, true);
-generate_stubgen!(fungible_iface, UniqueFungibleCall, false);
pallets/nft/src/eth/mod.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/mod.rs
+++ b/pallets/nft/src/eth/mod.rs
@@ -1,86 +1,21 @@
-pub mod account;
-pub mod erc;
pub mod sponsoring;
-use pallet_evm_coder_substrate::call_internal;
+use pallet_common::{
+ CollectionById,
+ erc::CommonEvmHandler,
+ eth::{map_eth_to_id, map_eth_to_token_id},
+};
+use pallet_fungible::FungibleHandle;
+use pallet_nonfungible::NonfungibleHandle;
+use pallet_refungible::{RefungibleHandle, erc::RefungibleTokenHandle};
use sp_std::borrow::ToOwned;
use sp_std::vec::Vec;
use pallet_evm::{PrecompileOutput};
use sp_core::{H160, U256};
-use frame_support::storage::StorageMap;
-use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};
-use erc::{UniqueFungibleCall, UniqueNFTCall};
+use crate::{CollectionMode, Config, dispatch::Dispatched};
+use pallet_common::CollectionHandle;
pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);
-
-// 0x17c4e6453Cc49AAAaEACA894e6D9683e00000001 - collection
-// TODO: Unhardcode prefix
-const ETH_ACCOUNT_PREFIX: [u8; 16] = [
- 0x17, 0xc4, 0xe6, 0x45, 0x3c, 0xc4, 0x9a, 0xaa, 0xae, 0xac, 0xa8, 0x94, 0xe6, 0xd9, 0x68, 0x3e,
-];
-
-fn map_eth_to_id(eth: &H160) -> Option<CollectionId> {
- if eth[0..16] != ETH_ACCOUNT_PREFIX {
- return None;
- }
- let mut id_bytes = [0; 4];
- id_bytes.copy_from_slice(ð[16..20]);
- Some(u32::from_be_bytes(id_bytes))
-}
-pub fn collection_id_to_address(id: u32) -> H160 {
- let mut out = [0; 20];
- out[0..16].copy_from_slice(Ð_ACCOUNT_PREFIX);
- out[16..20].copy_from_slice(&u32::to_be_bytes(id));
- H160(out)
-}
-
-/*
-fn call_internal<T: Config>(
- collection: &mut CollectionHandle<T>,
- caller: caller,
- method_id: u32,
- mut input: AbiReader,
- value: U256,
-) -> Result<Option<AbiWriter>> {
- match collection.mode.clone() {
- CollectionMode::Fungible(_) => {
- call_internal();
- let call = match UniqueFungibleCall::parse(method_id, &mut input)? {
- Some(v) => v,
- None => {
- #[cfg(feature = "std")]
- {
- println!("Method not found");
- }
- return Ok(None);
- }
- };
- Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(
- collection,
- Msg {
- call,
- caller,
- value,
- },
- )?))
- }
- CollectionMode::NFT => {
- let call = match UniqueNFTCall::parse(method_id, &mut input)? {
- Some(v) => v,
- None => return Ok(None),
- };
- Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(
- collection,
- Msg {
- call,
- caller,
- value,
- },
- )?))
- }
- _ => Err("erc calls only supported to fungible and nft collections for now".into()),
- }
-}*/
impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {
fn is_reserved(target: &H160) -> bool {
@@ -92,20 +27,26 @@
.unwrap_or(false)
}
fn get_code(target: &H160) -> Option<Vec<u8>> {
- map_eth_to_id(target)
- .and_then(<CollectionById<T>>::get)
- .map(|collection| {
+ if let Some(collection_id) = map_eth_to_id(target) {
+ let collection = <CollectionById<T>>::get(collection_id)?;
+ Some(
match collection.mode {
- CollectionMode::NFT => include_bytes!("stubs/UniqueNFT.raw") as &[u8],
- CollectionMode::Fungible(_) => {
- include_bytes!("stubs/UniqueFungible.raw") as &[u8]
- }
- CollectionMode::ReFungible => {
- include_bytes!("stubs/UniqueRefungible.raw") as &[u8]
- }
+ CollectionMode::NFT => <NonfungibleHandle<T>>::CODE,
+ CollectionMode::Fungible(_) => <FungibleHandle<T>>::CODE,
+ CollectionMode::ReFungible => <RefungibleHandle<T>>::CODE,
}
- .to_owned()
- })
+ .to_owned(),
+ )
+ } else if let Some((collection_id, _token_id)) = map_eth_to_token_id(target) {
+ let collection = <CollectionById<T>>::get(collection_id)?;
+ if collection.mode != CollectionMode::ReFungible {
+ return None;
+ }
+ // TODO: check token existence
+ Some(<RefungibleTokenHandle<T>>::CODE.to_owned())
+ } else {
+ None
+ }
}
fn call(
source: &H160,
@@ -114,17 +55,26 @@
input: &[u8],
value: U256,
) -> Option<PrecompileOutput> {
- let mut collection = map_eth_to_id(target)
- .and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;
- let result = match collection.mode {
- CollectionMode::NFT => {
- call_internal::<UniqueNFTCall, _>(*source, &mut collection, value, input)
+ if let Some(collection_id) = map_eth_to_id(target) {
+ let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
+ let dispatched = Dispatched::dispatch(collection);
+
+ match dispatched {
+ Dispatched::Fungible(h) => h.call(source, input, value),
+ Dispatched::Nonfungible(h) => h.call(source, input, value),
+ Dispatched::Refungible(h) => h.call(source, input, value),
}
- CollectionMode::Fungible(_) => {
- call_internal::<UniqueFungibleCall, _>(*source, &mut collection, value, input)
+ } else if let Some((collection_id, token_id)) = map_eth_to_token_id(target) {
+ let collection = <CollectionHandle<T>>::new_with_gas_limit(collection_id, gas_limit)?;
+ if collection.mode != CollectionMode::ReFungible {
+ return None;
}
- _ => return None,
- };
- collection.recorder.evm_to_precompile_output(result)
+
+ let handle = RefungibleHandle::cast(collection);
+ // TODO: check token existence
+ RefungibleTokenHandle(handle, token_id).call(source, input, value)
+ } else {
+ None
+ }
}
}
pallets/nft/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/sponsoring.rs
+++ b/pallets/nft/src/eth/sponsoring.rs
@@ -1,29 +1,30 @@
//! Implements EVM sponsoring logic via OnChargeEVMTransaction
-use crate::{
- Collection, CollectionById, Config, FungibleTransferBasket, NftTransferBasket,
- eth::{account::EvmBackwardsAddressMapping, map_eth_to_id},
-};
+use crate::{Collection, Config, FungibleTransferBasket, NftTransferBasket};
use evm_coder::{Call, abi::AbiReader};
use frame_support::{
- storage::{StorageMap, StorageDoubleMap},
+ storage::{StorageDoubleMap},
};
+use pallet_common::eth::map_eth_to_id;
use sp_core::H160;
use sp_std::prelude::*;
use up_sponsorship::SponsorshipHandler;
-use super::{
- account::CrossAccountId,
- erc::{UniqueFungibleCall, UniqueNFTCall, ERC721Call, ERC20Call, ERC721UniqueExtensionsCall},
-};
-use core::convert::TryInto;
use core::marker::PhantomData;
-use nft_data_structs::{NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
+use core::convert::TryInto;
+use nft_data_structs::{CollectionId, NFT_SPONSOR_TRANSFER_TIMEOUT, FUNGIBLE_SPONSOR_TRANSFER_TIMEOUT};
+use pallet_common::{
+ CollectionById,
+ account::{CrossAccountId, EvmBackwardsAddressMapping},
+};
+
+use pallet_nonfungible::erc::{UniqueNFTCall, ERC721UniqueExtensionsCall, ERC721Call};
+use pallet_fungible::erc::{UniqueFungibleCall, ERC20Call};
struct AnyError;
fn try_sponsor<T: Config>(
caller: &H160,
- collection_id: u32,
+ collection_id: CollectionId,
collection: &Collection<T>,
call: &[u8],
) -> Result<(), AnyError> {
pallets/nft/src/eth/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/UniqueFungible.sol
+++ /dev/null
@@ -1,121 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-// Common stubs holder
-contract Dummy {
- uint8 dummy;
- string stub_error = "this contract is implemented in native";
-}
-
-// Inline
-contract ERC20Events {
- event Transfer(address indexed from, address indexed to, uint256 value);
- event Approval(
- address indexed owner,
- address indexed spender,
- uint256 value
- );
-}
-
-// Inline
-contract InlineNameSymbol is Dummy {
- // Selector: name() 06fdde03
- function name() public view returns (string memory) {
- require(false, stub_error);
- dummy;
- return "";
- }
-
- // Selector: symbol() 95d89b41
- function symbol() public view returns (string memory) {
- require(false, stub_error);
- dummy;
- return "";
- }
-}
-
-// Inline
-contract InlineTotalSupply is Dummy {
- // Selector: totalSupply() 18160ddd
- function totalSupply() public view returns (uint256) {
- require(false, stub_error);
- dummy;
- return 0;
- }
-}
-
-contract ERC165 is Dummy {
- // Selector: supportsInterface(bytes4) 01ffc9a7
- function supportsInterface(uint32 interfaceId) public view returns (bool) {
- require(false, stub_error);
- interfaceId;
- dummy;
- return false;
- }
-}
-
-contract ERC20 is Dummy, InlineNameSymbol, InlineTotalSupply, ERC20Events {
- // Selector: decimals() 313ce567
- function decimals() public view returns (uint8) {
- require(false, stub_error);
- dummy;
- return 0;
- }
-
- // Selector: balanceOf(address) 70a08231
- function balanceOf(address owner) public view returns (uint256) {
- require(false, stub_error);
- owner;
- dummy;
- return 0;
- }
-
- // Selector: transfer(address,uint256) a9059cbb
- function transfer(address to, uint256 amount) public returns (bool) {
- require(false, stub_error);
- to;
- amount;
- dummy = 0;
- return false;
- }
-
- // Selector: transferFrom(address,address,uint256) 23b872dd
- function transferFrom(
- address from,
- address to,
- uint256 amount
- ) public returns (bool) {
- require(false, stub_error);
- from;
- to;
- amount;
- dummy = 0;
- return false;
- }
-
- // Selector: approve(address,uint256) 095ea7b3
- function approve(address spender, uint256 amount) public returns (bool) {
- require(false, stub_error);
- spender;
- amount;
- dummy = 0;
- return false;
- }
-
- // Selector: allowance(address,address) dd62ed3e
- function allowance(address owner, address spender)
- public
- view
- returns (uint256)
- {
- require(false, stub_error);
- owner;
- spender;
- dummy;
- return 0;
- }
-}
-
-contract UniqueFungible is Dummy, ERC165, ERC20 {}
pallets/nft/src/eth/stubs/UniqueNFT.rawdiffbeforeafterbothbinary blob — no preview
pallets/nft/src/eth/stubs/UniqueNFT.soldiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/UniqueNFT.sol
+++ /dev/null
@@ -1,326 +0,0 @@
-// SPDX-License-Identifier: OTHER
-// This code is automatically generated
-
-pragma solidity >=0.8.0 <0.9.0;
-
-// Anonymous struct
-struct Tuple0 {
- uint256 field_0;
- string field_1;
-}
-
-// Common stubs holder
-contract Dummy {
- uint8 dummy;
- string stub_error = "this contract is implemented in native";
-}
-
-// Inline
-contract ERC721Events {
- event Transfer(
- address indexed from,
- address indexed to,
- uint256 indexed tokenId
- );
- event Approval(
- address indexed owner,
- address indexed approved,
- uint256 indexed tokenId
- );
- event ApprovalForAll(
- address indexed owner,
- address indexed operator,
- bool approved
- );
-}
-
-// Inline
-contract ERC721MintableEvents {
- event MintingFinished();
-}
-
-// Inline
-contract InlineNameSymbol is Dummy {
- // Selector: name() 06fdde03
- function name() public view returns (string memory) {
- require(false, stub_error);
- dummy;
- return "";
- }
-
- // Selector: symbol() 95d89b41
- function symbol() public view returns (string memory) {
- require(false, stub_error);
- dummy;
- return "";
- }
-}
-
-// Inline
-contract InlineTotalSupply is Dummy {
- // Selector: totalSupply() 18160ddd
- function totalSupply() public view returns (uint256) {
- require(false, stub_error);
- dummy;
- return 0;
- }
-}
-
-contract ERC165 is Dummy {
- // Selector: supportsInterface(bytes4) 01ffc9a7
- function supportsInterface(uint32 interfaceId) public view returns (bool) {
- require(false, stub_error);
- interfaceId;
- dummy;
- return false;
- }
-}
-
-contract ERC721 is Dummy, ERC165, ERC721Events {
- // Selector: balanceOf(address) 70a08231
- function balanceOf(address owner) public view returns (uint256) {
- require(false, stub_error);
- owner;
- dummy;
- return 0;
- }
-
- // Selector: ownerOf(uint256) 6352211e
- function ownerOf(uint256 tokenId) public view returns (address) {
- require(false, stub_error);
- tokenId;
- dummy;
- return 0x0000000000000000000000000000000000000000;
- }
-
- // Selector: safeTransferFromWithData(address,address,uint256,bytes) 60a11672
- function safeTransferFromWithData(
- address from,
- address to,
- uint256 tokenId,
- bytes memory data
- ) public {
- require(false, stub_error);
- from;
- to;
- tokenId;
- data;
- dummy = 0;
- }
-
- // Selector: safeTransferFrom(address,address,uint256) 42842e0e
- function safeTransferFrom(
- address from,
- address to,
- uint256 tokenId
- ) public {
- require(false, stub_error);
- from;
- to;
- tokenId;
- dummy = 0;
- }
-
- // Selector: transferFrom(address,address,uint256) 23b872dd
- function transferFrom(
- address from,
- address to,
- uint256 tokenId
- ) public {
- require(false, stub_error);
- from;
- to;
- tokenId;
- dummy = 0;
- }
-
- // Selector: approve(address,uint256) 095ea7b3
- function approve(address approved, uint256 tokenId) public {
- require(false, stub_error);
- approved;
- tokenId;
- dummy = 0;
- }
-
- // Selector: setApprovalForAll(address,bool) a22cb465
- function setApprovalForAll(address operator, bool approved) public {
- require(false, stub_error);
- operator;
- approved;
- dummy = 0;
- }
-
- // Selector: getApproved(uint256) 081812fc
- function getApproved(uint256 tokenId) public view returns (address) {
- require(false, stub_error);
- tokenId;
- dummy;
- return 0x0000000000000000000000000000000000000000;
- }
-
- // Selector: isApprovedForAll(address,address) e985e9c5
- function isApprovedForAll(address owner, address operator)
- public
- view
- returns (address)
- {
- require(false, stub_error);
- owner;
- operator;
- dummy;
- return 0x0000000000000000000000000000000000000000;
- }
-}
-
-contract ERC721Burnable is Dummy {
- // Selector: burn(uint256) 42966c68
- function burn(uint256 tokenId) public {
- require(false, stub_error);
- tokenId;
- dummy = 0;
- }
-}
-
-contract ERC721Enumerable is Dummy, InlineTotalSupply {
- // Selector: tokenByIndex(uint256) 4f6ccce7
- function tokenByIndex(uint256 index) public view returns (uint256) {
- require(false, stub_error);
- index;
- dummy;
- return 0;
- }
-
- // Selector: tokenOfOwnerByIndex(address,uint256) 2f745c59
- function tokenOfOwnerByIndex(address owner, uint256 index)
- public
- view
- returns (uint256)
- {
- require(false, stub_error);
- owner;
- index;
- dummy;
- return 0;
- }
-}
-
-contract ERC721Metadata is Dummy, InlineNameSymbol {
- // Selector: tokenURI(uint256) c87b56dd
- function tokenURI(uint256 tokenId) public view returns (string memory) {
- require(false, stub_error);
- tokenId;
- dummy;
- return "";
- }
-}
-
-contract ERC721Mintable is Dummy, ERC721MintableEvents {
- // Selector: mintingFinished() 05d2035b
- function mintingFinished() public view returns (bool) {
- require(false, stub_error);
- dummy;
- return false;
- }
-
- // Selector: mint(address,uint256) 40c10f19
- function mint(address to, uint256 tokenId) public returns (bool) {
- require(false, stub_error);
- to;
- tokenId;
- dummy = 0;
- return false;
- }
-
- // Selector: mintWithTokenURI(address,uint256,string) 50bb4e7f
- function mintWithTokenURI(
- address to,
- uint256 tokenId,
- string memory tokenUri
- ) public returns (bool) {
- require(false, stub_error);
- to;
- tokenId;
- tokenUri;
- dummy = 0;
- return false;
- }
-
- // Selector: finishMinting() 7d64bcb4
- function finishMinting() public returns (bool) {
- require(false, stub_error);
- dummy = 0;
- return false;
- }
-}
-
-contract ERC721UniqueExtensions is Dummy {
- // Selector: transfer(address,uint256) a9059cbb
- function transfer(address to, uint256 tokenId) public {
- require(false, stub_error);
- to;
- tokenId;
- dummy = 0;
- }
-
- // Selector: nextTokenId() 75794a3c
- function nextTokenId() public view returns (uint256) {
- require(false, stub_error);
- dummy;
- return 0;
- }
-
- // Selector: setVariableMetadata(uint256,bytes) d4eac26d
- function setVariableMetadata(uint256 tokenId, bytes memory data) public {
- require(false, stub_error);
- tokenId;
- data;
- dummy = 0;
- }
-
- // Selector: getVariableMetadata(uint256) e6c5ce6f
- function getVariableMetadata(uint256 tokenId)
- public
- view
- returns (bytes memory)
- {
- require(false, stub_error);
- tokenId;
- dummy;
- return hex"";
- }
-
- // Selector: mintBulk(address,uint256[]) 44a9945e
- function mintBulk(address to, uint256[] memory tokenIds)
- public
- returns (bool)
- {
- require(false, stub_error);
- to;
- tokenIds;
- dummy = 0;
- return false;
- }
-
- // Selector: mintBulkWithTokenURI(address,(uint256,string)[]) 36543006
- function mintBulkWithTokenURI(address to, Tuple0[] memory tokens)
- public
- returns (bool)
- {
- require(false, stub_error);
- to;
- tokens;
- dummy = 0;
- return false;
- }
-}
-
-contract UniqueNFT is
- Dummy,
- ERC165,
- ERC721,
- ERC721Metadata,
- ERC721Enumerable,
- ERC721UniqueExtensions,
- ERC721Mintable,
- ERC721Burnable
-{}
pallets/nft/src/eth/stubs/UniqueRefungible.rawdiffbeforeafterboth--- a/pallets/nft/src/eth/stubs/UniqueRefungible.raw
+++ /dev/null
@@ -1 +0,0 @@
-TODO
\ No newline at end of file
pallets/nft/src/lib.rsdiffbeforeafterboth69decl_error! {69decl_error! {70 /// Error for non-fungible-token module.70 /// Error for non-fungible-token module.71 pub enum Error for Module<T: Config> {71 pub enum Error for Module<T: Config> {72 /// Total collections bound exceeded.73 TotalCollectionsLimitExceeded,74 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.72 /// Decimal_points parameter must be lower than MAX_DECIMAL_POINTS constant, currently it is 30.75 CollectionDecimalPointLimitExceeded,73 CollectionDecimalPointLimitExceeded,76 /// Collection name can not be longer than 63 char.77 CollectionNameLimitExceeded,78 /// Collection description can not be longer than 255 char.79 CollectionDescriptionLimitExceeded,80 /// Token prefix can not be longer than 15 char.81 CollectionTokenPrefixLimitExceeded,82 /// This collection does not exist.83 CollectionNotFound,84 /// Item not exists.85 TokenNotFound,86 /// Admin not found87 AdminNotFound,88 /// Arithmetic calculation overflow.89 NumOverflow,90 /// Account already has admin role.91 AlreadyAdmin,92 /// You do not own this collection.93 NoPermission,94 /// This address is not set as sponsor, use setCollectionSponsor first.74 /// This address is not set as sponsor, use setCollectionSponsor first.95 ConfirmUnsetSponsorFail,75 ConfirmUnsetSponsorFail,96 /// Collection is not in mint mode.97 PublicMintingNotAllowed,98 /// Sender parameter and item owner must be equal.99 MustBeTokenOwner,100 /// Item balance not enough.101 TokenValueTooLow,102 /// Size of item is too large.103 NftSizeLimitExceeded,104 /// No approve found105 ApproveNotFound,106 /// Requested value more than approved.107 TokenValueNotEnough,108 /// Only approved addresses can call this method.109 ApproveRequired,110 /// Address is not in white list.111 AddresNotInWhiteList,112 /// Number of collection admins bound exceeded.113 CollectionAdminsLimitExceeded,114 /// Owned tokens by a single address bound exceeded.115 AddressOwnershipLimitExceeded,116 /// Length of items properties must be greater than 0.76 /// Length of items properties must be greater than 0.117 EmptyArgument,77 EmptyArgument,118 /// const_data exceeded data limit.119 TokenConstDataLimitExceeded,120 /// variable_data exceeded data limit.121 TokenVariableDataLimitExceeded,122 /// Not NFT item data used to mint in NFT collection.123 NotNftDataUsedToMintNftCollectionToken,124 /// Not Fungible item data used to mint in Fungible collection.125 NotFungibleDataUsedToMintFungibleCollectionToken,126 /// Not Re Fungible item data used to mint in Re Fungible collection.127 NotReFungibleDataUsedToMintReFungibleCollectionToken,128 /// Unexpected collection type.129 UnexpectedCollectionType,130 /// Can't store metadata in fungible tokens.131 CantStoreMetadataInFungibleTokens,132 /// Collection token limit exceeded133 CollectionTokenLimitExceeded,134 /// Account token limit exceeded per collection135 AccountTokenLimitExceeded,136 /// Collection limit bounds per collection exceeded78 /// Collection limit bounds per collection exceeded137 CollectionLimitBoundsExceeded,79 CollectionLimitBoundsExceeded,138 /// Tried to enable permissions which are only permitted to be disabled80 /// Tried to enable permissions which are only permitted to be disabled139 OwnerPermissionsCantBeReverted,81 OwnerPermissionsCantBeReverted,140 /// Schema data size limit bound exceeded141 SchemaDataLimitExceeded,142 /// Maximum refungibility exceeded143 WrongRefungiblePieces,144 /// createRefungible should be called with one owner145 BadCreateRefungibleCall,146 /// Gas limit exceeded147 OutOfGas,148 /// Metadata update denied by collection settings149 MetadataUpdateDenied,150 /// Metadata update flag become unmutable with None option151 MetadataFlagFrozen,152 /// Collection settings not allowing items transferring153 TransferNotAllowed,154 /// Can't transfer tokens to ethereum zero address155 AddressIsZero,156 }82 }157}83}158159#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]160pub struct CollectionHandle<T: Config> {161 pub id: CollectionId,162 collection: Collection<T>,163 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,164}165impl<T: Config> CollectionHandle<T> {166 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {167 <CollectionById<T>>::get(id).map(|collection| Self {168 id,169 collection,170 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(171 eth::collection_id_to_address(id),172 gas_limit,173 ),174 })175 }176 pub fn get(id: CollectionId) -> Option<Self> {177 Self::get_with_gas_limit(id, u64::MAX)178 }179 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {180 self.recorder.log_sub(log)181 }182 #[allow(dead_code)]183 fn consume_gas(&self, gas: u64) -> DispatchResult {184 self.recorder.consume_gas_sub(gas)185 }186 fn consume_sload(&self) -> DispatchResult {187 self.recorder.consume_sload_sub()188 }189 fn consume_sstore(&self) -> DispatchResult {190 self.recorder.consume_sstore_sub()191 }192 pub fn submit_logs(self) -> DispatchResult {193 self.recorder.submit_logs()194 }195 pub fn save(self) -> DispatchResult {196 self.recorder.submit_logs()?;197 <CollectionById<T>>::insert(self.id, self.collection);198 Ok(())199 }200}201impl<T: Config> Deref for CollectionHandle<T> {202 type Target = Collection<T>;203204 fn deref(&self) -> &Self::Target {205 &self.collection206 }207}208209impl<T: Config> DerefMut for CollectionHandle<T> {210 fn deref_mut(&mut self) -> &mut Self::Target {211 &mut self.collection212 }213}214215pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {84pub trait Config:85 system::Config86 + pallet_evm_coder_substrate::Config87 + pallet_common::Config216 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;88 + pallet_nonfungible::Config21789 + pallet_refungible::Config90 + pallet_fungible::Config91 + Sized92{218 /// Weight information for extrinsics in this pallet.93 /// Weight information for extrinsics in this pallet.219 type WeightInfo: WeightInfo;94 type WeightInfo: WeightInfo;22095}221 type EvmAddressMapping: pallet_evm::AddressMapping<Self::AccountId>;222 type EvmBackwardsAddressMapping: EvmBackwardsAddressMapping<Self::AccountId>;223224 type CrossAccountId: CrossAccountId<Self::AccountId>;225 type Currency: Currency<Self::AccountId>;226 type CollectionCreationPrice: Get<227 <<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,228 >;229 type TreasuryAccountId: Get<Self::AccountId>;230}23196232type SelfWeightOf<T> = <T as Config>::WeightInfo;97type SelfWeightOf<T> = <T as Config>::WeightInfo;23398293 trait Store for Module<T: Config> as Nft {158 trait Store for Module<T: Config> as Nft {294159295 //#region Private members160 //#region Private members296 /// Id of next collection297 CreatedCollectionCount: u32;298 /// Used for migrations161 /// Used for migrations299 ChainVersion: u64;162 ChainVersion: u64;300 /// Id of last collection token301 /// Collection id (controlled?1)302 ItemListIndex: map hasher(blake2_128_concat) CollectionId => TokenId;303 //#endregion163 //#endregion304305 //#region Bound counters306 /// Amount of collections destroyed, used for total amount tracking with307 /// CreatedCollectionCount308 DestroyedCollectionCount: u32;309 //#endregion310311 //#region Basic collections312 /// Collection info313 /// Collection id (controlled?1)314 pub CollectionById get(fn collection_id) config(): map hasher(blake2_128_concat) CollectionId => Option<Collection<T>> = None;315 /// List of collection admins316 /// Collection id (controlled?2)317 pub AdminList get(fn admin_list_collection): map hasher(blake2_128_concat) CollectionId => Vec<T::CrossAccountId>;318 /// Whitelisted collection users319 /// Collection id (controlled?2), user id (controlled?3)320 pub WhiteList get(fn white_list): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => bool;321 //#endregion322323 /// How many of collection items user have324 /// Collection id (controlled?2), account id (real)325 pub Balance get(fn balance_count): double_map hasher(blake2_128_concat) CollectionId, hasher(twox_64_concat) T::AccountId => u128;326327 /// Amount of items which spender can transfer out of owners account (via transferFrom)328 /// Collection id (controlled?2), (token id (controlled ?2) + owner account id (real) + spender account id (controlled?3))329 /// TODO: Off chain worker should remove from this map when token gets removed330 pub Allowances get(fn approved): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) (TokenId, T::AccountId, T::AccountId) => u128;331332 //#region Item collections333 /// Collection id (controlled?2), token id (controlled?1)334 pub NftItemList get(fn nft_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<NftItemType<T::CrossAccountId>>;335 /// Collection id (controlled?2), owner (controlled?2)336 pub FungibleItemList get(fn fungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => FungibleItemType;337 /// Collection id (controlled?2), token id (controlled?1)338 pub ReFungibleItemList get(fn refungible_item_id) config(): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<ReFungibleItemType<T::CrossAccountId>>;339 //#endregion340341 //#region Index list342 /// Collection id (controlled?2), tokens owner (controlled?2)343 pub AddressTokens get(fn address_tokens): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) T::AccountId => Vec<TokenId>;344 //#endregion345164346 //#region Tokens transfer rate limit baskets165 //#region Tokens transfer rate limit baskets347 /// (Collection id (controlled?2), who created (real))166 /// (Collection id (controlled?2), who created (real))359 /// Collection id (controlled?2), token id (controlled?2)178 /// Collection id (controlled?2), token id (controlled?2)360 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;179 pub VariableMetaDataBasket get(fn variable_meta_data_basket): double_map hasher(blake2_128_concat) CollectionId, hasher(blake2_128_concat) TokenId => Option<T::BlockNumber> = None;361 }180 }362 add_extra_genesis {363 build(|config: &GenesisConfig<T>| {364 // Modification of storage365 for (_num, _c) in &config.collection_id {366 <Module<T>>::init_collection(_c);367 }368369 for (_num, _c, _i) in &config.nft_item_id {370 <Module<T>>::init_nft_token(*_c, _i);371 }372373 for (collection_id, account_id, fungible_item) in &config.fungible_item_id {374 <Module<T>>::init_fungible_token(*collection_id, &T::CrossAccountId::from_sub(account_id.clone()), fungible_item);375 }376377 for (_num, _c, _i) in &config.refungible_item_id {378 <Module<T>>::init_refungible_token(*_c, _i);379 }380 })381 }382}181}383384decl_event!(385 pub enum Event<T>386 where387 AccountId = <T as frame_system::Config>::AccountId,388 CrossAccountId = <T as Config>::CrossAccountId,389 {390 /// New collection was created391 ///392 /// # Arguments393 ///394 /// * collection_id: Globally unique identifier of newly created collection.395 ///396 /// * mode: [CollectionMode] converted into u8.397 ///398 /// * account_id: Collection owner.399 CollectionCreated(CollectionId, u8, AccountId),400401 /// New item was created.402 ///403 /// # Arguments404 ///405 /// * collection_id: Id of the collection where item was created.406 ///407 /// * item_id: Id of an item. Unique within the collection.408 ///409 /// * recipient: Owner of newly created item410 ItemCreated(CollectionId, TokenId, CrossAccountId),411412 /// Collection item was burned.413 ///414 /// # Arguments415 ///416 /// collection_id.417 ///418 /// item_id: Identifier of burned NFT.419 ItemDestroyed(CollectionId, TokenId),420421 /// Item was transferred422 ///423 /// * collection_id: Id of collection to which item is belong424 ///425 /// * item_id: Id of an item426 ///427 /// * sender: Original owner of item428 ///429 /// * recipient: New owner of item430 ///431 /// * amount: Always 1 for NFT432 Transfer(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),433434 /// * collection_id435 ///436 /// * item_id437 ///438 /// * sender439 ///440 /// * spender441 ///442 /// * amount443 Approved(CollectionId, TokenId, CrossAccountId, CrossAccountId, u128),444 }445);446182447decl_module! {183decl_module! {448 pub struct Module<T: Config> for enum Call184 pub struct Module<T: Config> for enum Call449 where185 where450 origin: T::Origin186 origin: T::Origin451 {187 {452 fn deposit_event() = default;453 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;188 const CollectionAdminsLimit: u64 = COLLECTION_ADMINS_LIMIT;454 type Error = Error<T>;189 type Error = Error<T>;455190