difftreelog
cargo fmt
in: master
10 files changed
pallets/common/src/lib.rsdiffbeforeafterboth--- a/pallets/common/src/lib.rs
+++ b/pallets/common/src/lib.rs
@@ -168,7 +168,9 @@
use scale_info::TypeInfo;
#[pallet::config]
- pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config {
+ pub trait Config:
+ frame_system::Config + pallet_evm_coder_substrate::Config + TypeInfo + account::Config
+ {
type Event: IsType<<Self as frame_system::Config>::Event> + From<Event<Self>>;
type Currency: Currency<Self::AccountId>;
pallets/evm-contract-helpers/src/eth.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/eth.rs
+++ b/pallets/evm-contract-helpers/src/eth.rs
@@ -18,7 +18,8 @@
use evm_coder::{abi::AbiWriter, execution::Result, generate_stubgen, solidity_interface, types::*};
use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};
use pallet_evm::{
- ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure, account::CrossAccountId
+ ExitRevert, OnCreate, OnMethodCall, PrecompileResult, PrecompileFailure,
+ account::CrossAccountId,
};
use sp_core::H160;
use crate::{
@@ -179,7 +180,9 @@
}
pub struct HelpersContractSponsoring<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for HelpersContractSponsoring<T> {
+impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
+ for HelpersContractSponsoring<T>
+{
fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
let mode = <Pallet<T>>::sponsoring_mode(call.0);
if mode == SponsoringModeT::Disabled {
pallets/evm-contract-helpers/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-contract-helpers/src/lib.rs
+++ b/pallets/evm-contract-helpers/src/lib.rs
@@ -30,7 +30,9 @@
use sp_core::H160;
#[pallet::config]
- pub trait Config: frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config {
+ pub trait Config:
+ frame_system::Config + pallet_evm_coder_substrate::Config + pallet_evm::account::Config
+ {
type ContractAddress: Get<H160>;
type DefaultSponsoringRateLimit: Get<Self::BlockNumber>;
}
pallets/evm-transaction-payment/src/lib.rsdiffbeforeafterboth--- a/pallets/evm-transaction-payment/src/lib.rs
+++ b/pallets/evm-transaction-payment/src/lib.rs
@@ -87,7 +87,8 @@
fee: U256,
) -> core::result::Result<Self::LiquidityInfo, pallet_evm::Error<T>> {
let who_pays_fee = if let WithdrawReason::Call { target, input } = &reason {
- T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone())).unwrap_or(who.clone())
+ T::EvmSponsorshipHandler::get_sponsor(who, &(*target, input.clone()))
+ .unwrap_or(who.clone())
} else {
who.clone()
};
pallets/fungible/src/lib.rsdiffbeforeafterboth--- a/pallets/fungible/src/lib.rs
+++ b/pallets/fungible/src/lib.rs
@@ -19,9 +19,7 @@
use core::ops::Deref;
use frame_support::{ensure};
use up_data_structs::{AccessMode, CollectionId, TokenId, CreateCollectionData};
-use pallet_common::{
- Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
-};
+use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};
use pallet_evm::account::CrossAccountId;
use pallet_evm_coder_substrate::WithRecorder;
use sp_core::H160;
pallets/nonfungible/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 erc::ERC721Events;20use frame_support::{BoundedVec, ensure};21use up_data_structs::{22 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,23};24use pallet_common::{25 Error as CommonError, Pallet as PalletCommon, Event as CommonEvent,26};27use pallet_evm::account::CrossAccountId;28use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};29use sp_core::H160;30use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};31use sp_std::{vec::Vec, vec};32use core::ops::Deref;33use sp_std::collections::btree_map::BTreeMap;34use codec::{Encode, Decode, MaxEncodedLen};35use scale_info::TypeInfo;3637pub use pallet::*;38#[cfg(feature = "runtime-benchmarks")]39pub mod benchmarking;40pub mod common;41pub mod erc;42pub mod weights;4344pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;45pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4647#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]48pub struct ItemData<CrossAccountId> {49 pub const_data: BoundedVec<u8, CustomDataLimit>,50 pub variable_data: BoundedVec<u8, CustomDataLimit>,51 pub owner: CrossAccountId,52}5354#[frame_support::pallet]55pub mod pallet {56 use super::*;57 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};58 use up_data_structs::{CollectionId, TokenId};59 use super::weights::WeightInfo;6061 #[pallet::error]62 pub enum Error<T> {63 /// Not Nonfungible item data used to mint in Nonfungible collection.64 NotNonfungibleDataUsedToMintFungibleCollectionToken,65 /// Used amount > 1 with NFT66 NonfungibleItemsHaveNoAmount,67 }6869 #[pallet::config]70 pub trait Config: frame_system::Config + pallet_common::Config {71 type WeightInfo: WeightInfo;72 }7374 #[pallet::pallet]75 #[pallet::generate_store(pub(super) trait Store)]76 pub struct Pallet<T>(_);7778 #[pallet::storage]79 pub type TokensMinted<T: Config> =80 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;81 #[pallet::storage]82 pub type TokensBurnt<T: Config> =83 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;8485 #[pallet::storage]86 pub type TokenData<T: Config> = StorageNMap<87 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),88 Value = ItemData<T::CrossAccountId>,89 QueryKind = OptionQuery,90 >;9192 /// Used to enumerate tokens owned by account93 #[pallet::storage]94 pub type Owned<T: Config> = StorageNMap<95 Key = (96 Key<Twox64Concat, CollectionId>,97 Key<Blake2_128Concat, T::CrossAccountId>,98 Key<Twox64Concat, TokenId>,99 ),100 Value = bool,101 QueryKind = ValueQuery,102 >;103104 #[pallet::storage]105 pub type AccountBalance<T: Config> = StorageNMap<106 Key = (107 Key<Twox64Concat, CollectionId>,108 Key<Blake2_128Concat, T::CrossAccountId>,109 ),110 Value = u32,111 QueryKind = ValueQuery,112 >;113114 #[pallet::storage]115 pub type Allowance<T: Config> = StorageNMap<116 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),117 Value = T::CrossAccountId,118 QueryKind = OptionQuery,119 >;120}121122pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);123impl<T: Config> NonfungibleHandle<T> {124 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {125 Self(inner)126 }127 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {128 self.0129 }130}131impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {132 fn recorder(&self) -> &SubstrateRecorder<T> {133 self.0.recorder()134 }135 fn into_recorder(self) -> SubstrateRecorder<T> {136 self.0.into_recorder()137 }138}139impl<T: Config> Deref for NonfungibleHandle<T> {140 type Target = pallet_common::CollectionHandle<T>;141142 fn deref(&self) -> &Self::Target {143 &self.0144 }145}146147impl<T: Config> Pallet<T> {148 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {149 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)150 }151 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {152 <TokenData<T>>::contains_key((collection.id, token))153 }154}155156// unchecked calls skips any permission checks157impl<T: Config> Pallet<T> {158 pub fn init_collection(159 owner: T::AccountId,160 data: CreateCollectionData<T::AccountId>,161 ) -> Result<CollectionId, DispatchError> {162 <PalletCommon<T>>::init_collection(owner, data)163 }164 pub fn destroy_collection(165 collection: NonfungibleHandle<T>,166 sender: &T::CrossAccountId,167 ) -> DispatchResult {168 let id = collection.id;169170 // =========171172 PalletCommon::destroy_collection(collection.0, sender)?;173174 <TokenData<T>>::remove_prefix((id,), None);175 <Owned<T>>::remove_prefix((id,), None);176 <TokensMinted<T>>::remove(id);177 <TokensBurnt<T>>::remove(id);178 <Allowance<T>>::remove_prefix((id,), None);179 <AccountBalance<T>>::remove_prefix((id,), None);180 Ok(())181 }182183 pub fn burn(184 collection: &NonfungibleHandle<T>,185 sender: &T::CrossAccountId,186 token: TokenId,187 ) -> DispatchResult {188 let token_data =189 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;190 ensure!(191 &token_data.owner == sender192 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),193 <CommonError<T>>::NoPermission194 );195196 if collection.access == AccessMode::AllowList {197 collection.check_allowlist(sender)?;198 }199200 let burnt = <TokensBurnt<T>>::get(collection.id)201 .checked_add(1)202 .ok_or(ArithmeticError::Overflow)?;203204 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))205 .checked_sub(1)206 .ok_or(ArithmeticError::Overflow)?;207208 if balance == 0 {209 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));210 } else {211 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);212 }213 // =========214215 <Owned<T>>::remove((collection.id, &token_data.owner, token));216 <TokensBurnt<T>>::insert(collection.id, burnt);217 <TokenData<T>>::remove((collection.id, token));218 let old_spender = <Allowance<T>>::take((collection.id, token));219220 if let Some(old_spender) = old_spender {221 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(222 collection.id,223 token,224 sender.clone(),225 old_spender,226 0,227 ));228 }229230 collection.log_mirrored(ERC721Events::Transfer {231 from: *token_data.owner.as_eth(),232 to: H160::default(),233 token_id: token.into(),234 });235 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(236 collection.id,237 token,238 token_data.owner,239 1,240 ));241 Ok(())242 }243244 pub fn transfer(245 collection: &NonfungibleHandle<T>,246 from: &T::CrossAccountId,247 to: &T::CrossAccountId,248 token: TokenId,249 ) -> DispatchResult {250 ensure!(251 collection.limits.transfers_enabled(),252 <CommonError<T>>::TransferNotAllowed253 );254255 let token_data =256 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;257 ensure!(258 &token_data.owner == from259 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),260 <CommonError<T>>::NoPermission261 );262263 if collection.access == AccessMode::AllowList {264 collection.check_allowlist(from)?;265 collection.check_allowlist(to)?;266 }267 <PalletCommon<T>>::ensure_correct_receiver(to)?;268269 let balance_from = <AccountBalance<T>>::get((collection.id, from))270 .checked_sub(1)271 .ok_or(<CommonError<T>>::TokenValueTooLow)?;272 let balance_to = if from != to {273 let balance_to = <AccountBalance<T>>::get((collection.id, to))274 .checked_add(1)275 .ok_or(ArithmeticError::Overflow)?;276277 ensure!(278 balance_to < collection.limits.account_token_ownership_limit(),279 <CommonError<T>>::AccountTokenLimitExceeded,280 );281282 Some(balance_to)283 } else {284 None285 };286287 // =========288289 <TokenData<T>>::insert(290 (collection.id, token),291 ItemData {292 owner: to.clone(),293 ..token_data294 },295 );296297 if let Some(balance_to) = balance_to {298 // from != to299 if balance_from == 0 {300 <AccountBalance<T>>::remove((collection.id, from));301 } else {302 <AccountBalance<T>>::insert((collection.id, from), balance_from);303 }304 <AccountBalance<T>>::insert((collection.id, to), balance_to);305 <Owned<T>>::remove((collection.id, from, token));306 <Owned<T>>::insert((collection.id, to, token), true);307 }308 Self::set_allowance_unchecked(collection, from, token, None, true);309310 collection.log_mirrored(ERC721Events::Transfer {311 from: *from.as_eth(),312 to: *to.as_eth(),313 token_id: token.into(),314 });315 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(316 collection.id,317 token,318 from.clone(),319 to.clone(),320 1,321 ));322 Ok(())323 }324325 pub fn create_multiple_items(326 collection: &NonfungibleHandle<T>,327 sender: &T::CrossAccountId,328 data: Vec<CreateItemData<T>>,329 ) -> DispatchResult {330 if !collection.is_owner_or_admin(sender) {331 ensure!(332 collection.mint_mode,333 <CommonError<T>>::PublicMintingNotAllowed334 );335 collection.check_allowlist(sender)?;336337 for item in data.iter() {338 collection.check_allowlist(&item.owner)?;339 }340 }341342 for data in data.iter() {343 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;344 }345346 let first_token = <TokensMinted<T>>::get(collection.id);347 let tokens_minted = first_token348 .checked_add(data.len() as u32)349 .ok_or(ArithmeticError::Overflow)?;350 ensure!(351 tokens_minted <= collection.limits.token_limit(),352 <CommonError<T>>::CollectionTokenLimitExceeded353 );354355 let mut balances = BTreeMap::new();356 for data in &data {357 let balance = balances358 .entry(&data.owner)359 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));360 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;361362 ensure!(363 *balance <= collection.limits.account_token_ownership_limit(),364 <CommonError<T>>::AccountTokenLimitExceeded,365 );366 }367368 // =========369370 <TokensMinted<T>>::insert(collection.id, tokens_minted);371 for (account, balance) in balances {372 <AccountBalance<T>>::insert((collection.id, account), balance);373 }374 for (i, data) in data.into_iter().enumerate() {375 let token = first_token + i as u32 + 1;376377 <TokenData<T>>::insert(378 (collection.id, token),379 ItemData {380 const_data: data.const_data,381 variable_data: data.variable_data,382 owner: data.owner.clone(),383 },384 );385 <Owned<T>>::insert((collection.id, &data.owner, token), true);386387 collection.log_mirrored(ERC721Events::Transfer {388 from: H160::default(),389 to: *data.owner.as_eth(),390 token_id: token.into(),391 });392 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(393 collection.id,394 TokenId(token),395 data.owner.clone(),396 1,397 ));398 }399 Ok(())400 }401402 pub fn set_allowance_unchecked(403 collection: &NonfungibleHandle<T>,404 sender: &T::CrossAccountId,405 token: TokenId,406 spender: Option<&T::CrossAccountId>,407 assume_implicit_eth: bool,408 ) {409 if let Some(spender) = spender {410 let old_spender = <Allowance<T>>::get((collection.id, token));411 <Allowance<T>>::insert((collection.id, token), spender);412 // In ERC721 there is only one possible approved user of token, so we set413 // approved user to spender414 collection.log_mirrored(ERC721Events::Approval {415 owner: *sender.as_eth(),416 approved: *spender.as_eth(),417 token_id: token.into(),418 });419 // In Unique chain, any token can have any amount of approved users, so we need to420 // set allowance of old owner to 0, and allowance of new owner to 1421 if old_spender.as_ref() != Some(spender) {422 if let Some(old_owner) = old_spender {423 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(424 collection.id,425 token,426 sender.clone(),427 old_owner,428 0,429 ));430 }431 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(432 collection.id,433 token,434 sender.clone(),435 spender.clone(),436 1,437 ));438 }439 } else {440 let old_spender = <Allowance<T>>::take((collection.id, token));441 if !assume_implicit_eth {442 // In ERC721 there is only one possible approved user of token, so we set443 // approved user to zero address444 collection.log_mirrored(ERC721Events::Approval {445 owner: *sender.as_eth(),446 approved: H160::default(),447 token_id: token.into(),448 });449 }450 // In Unique chain, any token can have any amount of approved users, so we need to451 // set allowance of old owner to 0452 if let Some(old_spender) = old_spender {453 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(454 collection.id,455 token,456 sender.clone(),457 old_spender,458 0,459 ));460 }461 }462 }463464 pub fn set_allowance(465 collection: &NonfungibleHandle<T>,466 sender: &T::CrossAccountId,467 token: TokenId,468 spender: Option<&T::CrossAccountId>,469 ) -> DispatchResult {470 if collection.access == AccessMode::AllowList {471 collection.check_allowlist(sender)?;472 if let Some(spender) = spender {473 collection.check_allowlist(spender)?;474 }475 }476477 if let Some(spender) = spender {478 <PalletCommon<T>>::ensure_correct_receiver(spender)?;479 }480 let token_data =481 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;482 if &token_data.owner != sender {483 ensure!(484 collection.ignores_owned_amount(sender),485 <CommonError<T>>::CantApproveMoreThanOwned486 );487 }488489 // =========490491 Self::set_allowance_unchecked(collection, sender, token, spender, false);492 Ok(())493 }494495 pub fn transfer_from(496 collection: &NonfungibleHandle<T>,497 spender: &T::CrossAccountId,498 from: &T::CrossAccountId,499 to: &T::CrossAccountId,500 token: TokenId,501 ) -> DispatchResult {502 if spender.conv_eq(from) {503 return Self::transfer(collection, from, to, token);504 }505 if collection.access == AccessMode::AllowList {506 // `from`, `to` checked in [`transfer`]507 collection.check_allowlist(spender)?;508 }509510 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {511 ensure!(512 collection.ignores_allowance(spender),513 <CommonError<T>>::ApprovedValueTooLow514 );515 }516517 // =========518519 Self::transfer(collection, from, to, token)?;520 // Allowance is reset in [`transfer`]521 Ok(())522 }523524 pub fn burn_from(525 collection: &NonfungibleHandle<T>,526 spender: &T::CrossAccountId,527 from: &T::CrossAccountId,528 token: TokenId,529 ) -> DispatchResult {530 if spender.conv_eq(from) {531 return Self::burn(collection, from, token);532 }533 if collection.access == AccessMode::AllowList {534 // `from` checked in [`burn`]535 collection.check_allowlist(spender)?;536 }537538 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {539 ensure!(540 collection.ignores_allowance(spender),541 <CommonError<T>>::ApprovedValueTooLow542 );543 }544545 // =========546547 Self::burn(collection, from, token)548 }549550 pub fn set_variable_metadata(551 collection: &NonfungibleHandle<T>,552 sender: &T::CrossAccountId,553 token: TokenId,554 data: BoundedVec<u8, CustomDataLimit>,555 ) -> DispatchResult {556 let token_data =557 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;558 collection.check_can_update_meta(sender, &token_data.owner)?;559560 // =========561562 <TokenData<T>>::insert(563 (collection.id, token),564 ItemData {565 variable_data: data,566 ..token_data567 },568 );569 Ok(())570 }571572 /// Delegated to `create_multiple_items`573 pub fn create_item(574 collection: &NonfungibleHandle<T>,575 sender: &T::CrossAccountId,576 data: CreateItemData<T>,577 ) -> DispatchResult {578 Self::create_multiple_items(collection, sender, vec![data])579 }580}1// 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 erc::ERC721Events;20use frame_support::{BoundedVec, ensure};21use up_data_structs::{22 AccessMode, CollectionId, CustomDataLimit, TokenId, CreateCollectionData, CreateNftExData,23};24use pallet_common::{Error as CommonError, Pallet as PalletCommon, Event as CommonEvent};25use pallet_evm::account::CrossAccountId;26use pallet_evm_coder_substrate::{SubstrateRecorder, WithRecorder};27use sp_core::H160;28use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};29use sp_std::{vec::Vec, vec};30use core::ops::Deref;31use sp_std::collections::btree_map::BTreeMap;32use codec::{Encode, Decode, MaxEncodedLen};33use scale_info::TypeInfo;3435pub use pallet::*;36#[cfg(feature = "runtime-benchmarks")]37pub mod benchmarking;38pub mod common;39pub mod erc;40pub mod weights;4142pub type CreateItemData<T> = CreateNftExData<<T as pallet_evm::account::Config>::CrossAccountId>;43pub(crate) type SelfWeightOf<T> = <T as Config>::WeightInfo;4445#[derive(Encode, Decode, TypeInfo, MaxEncodedLen)]46pub struct ItemData<CrossAccountId> {47 pub const_data: BoundedVec<u8, CustomDataLimit>,48 pub variable_data: BoundedVec<u8, CustomDataLimit>,49 pub owner: CrossAccountId,50}5152#[frame_support::pallet]53pub mod pallet {54 use super::*;55 use frame_support::{Blake2_128Concat, Twox64Concat, pallet_prelude::*, storage::Key};56 use up_data_structs::{CollectionId, TokenId};57 use super::weights::WeightInfo;5859 #[pallet::error]60 pub enum Error<T> {61 /// Not Nonfungible item data used to mint in Nonfungible collection.62 NotNonfungibleDataUsedToMintFungibleCollectionToken,63 /// Used amount > 1 with NFT64 NonfungibleItemsHaveNoAmount,65 }6667 #[pallet::config]68 pub trait Config: frame_system::Config + pallet_common::Config {69 type WeightInfo: WeightInfo;70 }7172 #[pallet::pallet]73 #[pallet::generate_store(pub(super) trait Store)]74 pub struct Pallet<T>(_);7576 #[pallet::storage]77 pub type TokensMinted<T: Config> =78 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;79 #[pallet::storage]80 pub type TokensBurnt<T: Config> =81 StorageMap<Hasher = Twox64Concat, Key = CollectionId, Value = u32, QueryKind = ValueQuery>;8283 #[pallet::storage]84 pub type TokenData<T: Config> = StorageNMap<85 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),86 Value = ItemData<T::CrossAccountId>,87 QueryKind = OptionQuery,88 >;8990 /// Used to enumerate tokens owned by account91 #[pallet::storage]92 pub type Owned<T: Config> = StorageNMap<93 Key = (94 Key<Twox64Concat, CollectionId>,95 Key<Blake2_128Concat, T::CrossAccountId>,96 Key<Twox64Concat, TokenId>,97 ),98 Value = bool,99 QueryKind = ValueQuery,100 >;101102 #[pallet::storage]103 pub type AccountBalance<T: Config> = StorageNMap<104 Key = (105 Key<Twox64Concat, CollectionId>,106 Key<Blake2_128Concat, T::CrossAccountId>,107 ),108 Value = u32,109 QueryKind = ValueQuery,110 >;111112 #[pallet::storage]113 pub type Allowance<T: Config> = StorageNMap<114 Key = (Key<Twox64Concat, CollectionId>, Key<Twox64Concat, TokenId>),115 Value = T::CrossAccountId,116 QueryKind = OptionQuery,117 >;118}119120pub struct NonfungibleHandle<T: Config>(pallet_common::CollectionHandle<T>);121impl<T: Config> NonfungibleHandle<T> {122 pub fn cast(inner: pallet_common::CollectionHandle<T>) -> Self {123 Self(inner)124 }125 pub fn into_inner(self) -> pallet_common::CollectionHandle<T> {126 self.0127 }128}129impl<T: Config> WithRecorder<T> for NonfungibleHandle<T> {130 fn recorder(&self) -> &SubstrateRecorder<T> {131 self.0.recorder()132 }133 fn into_recorder(self) -> SubstrateRecorder<T> {134 self.0.into_recorder()135 }136}137impl<T: Config> Deref for NonfungibleHandle<T> {138 type Target = pallet_common::CollectionHandle<T>;139140 fn deref(&self) -> &Self::Target {141 &self.0142 }143}144145impl<T: Config> Pallet<T> {146 pub fn total_supply(collection: &NonfungibleHandle<T>) -> u32 {147 <TokensMinted<T>>::get(collection.id) - <TokensBurnt<T>>::get(collection.id)148 }149 pub fn token_exists(collection: &NonfungibleHandle<T>, token: TokenId) -> bool {150 <TokenData<T>>::contains_key((collection.id, token))151 }152}153154// unchecked calls skips any permission checks155impl<T: Config> Pallet<T> {156 pub fn init_collection(157 owner: T::AccountId,158 data: CreateCollectionData<T::AccountId>,159 ) -> Result<CollectionId, DispatchError> {160 <PalletCommon<T>>::init_collection(owner, data)161 }162 pub fn destroy_collection(163 collection: NonfungibleHandle<T>,164 sender: &T::CrossAccountId,165 ) -> DispatchResult {166 let id = collection.id;167168 // =========169170 PalletCommon::destroy_collection(collection.0, sender)?;171172 <TokenData<T>>::remove_prefix((id,), None);173 <Owned<T>>::remove_prefix((id,), None);174 <TokensMinted<T>>::remove(id);175 <TokensBurnt<T>>::remove(id);176 <Allowance<T>>::remove_prefix((id,), None);177 <AccountBalance<T>>::remove_prefix((id,), None);178 Ok(())179 }180181 pub fn burn(182 collection: &NonfungibleHandle<T>,183 sender: &T::CrossAccountId,184 token: TokenId,185 ) -> DispatchResult {186 let token_data =187 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;188 ensure!(189 &token_data.owner == sender190 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(sender)),191 <CommonError<T>>::NoPermission192 );193194 if collection.access == AccessMode::AllowList {195 collection.check_allowlist(sender)?;196 }197198 let burnt = <TokensBurnt<T>>::get(collection.id)199 .checked_add(1)200 .ok_or(ArithmeticError::Overflow)?;201202 let balance = <AccountBalance<T>>::get((collection.id, token_data.owner.clone()))203 .checked_sub(1)204 .ok_or(ArithmeticError::Overflow)?;205206 if balance == 0 {207 <AccountBalance<T>>::remove((collection.id, token_data.owner.clone()));208 } else {209 <AccountBalance<T>>::insert((collection.id, token_data.owner.clone()), balance);210 }211 // =========212213 <Owned<T>>::remove((collection.id, &token_data.owner, token));214 <TokensBurnt<T>>::insert(collection.id, burnt);215 <TokenData<T>>::remove((collection.id, token));216 let old_spender = <Allowance<T>>::take((collection.id, token));217218 if let Some(old_spender) = old_spender {219 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(220 collection.id,221 token,222 sender.clone(),223 old_spender,224 0,225 ));226 }227228 collection.log_mirrored(ERC721Events::Transfer {229 from: *token_data.owner.as_eth(),230 to: H160::default(),231 token_id: token.into(),232 });233 <PalletCommon<T>>::deposit_event(CommonEvent::ItemDestroyed(234 collection.id,235 token,236 token_data.owner,237 1,238 ));239 Ok(())240 }241242 pub fn transfer(243 collection: &NonfungibleHandle<T>,244 from: &T::CrossAccountId,245 to: &T::CrossAccountId,246 token: TokenId,247 ) -> DispatchResult {248 ensure!(249 collection.limits.transfers_enabled(),250 <CommonError<T>>::TransferNotAllowed251 );252253 let token_data =254 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;255 ensure!(256 &token_data.owner == from257 || (collection.limits.owner_can_transfer() && collection.is_owner_or_admin(from)),258 <CommonError<T>>::NoPermission259 );260261 if collection.access == AccessMode::AllowList {262 collection.check_allowlist(from)?;263 collection.check_allowlist(to)?;264 }265 <PalletCommon<T>>::ensure_correct_receiver(to)?;266267 let balance_from = <AccountBalance<T>>::get((collection.id, from))268 .checked_sub(1)269 .ok_or(<CommonError<T>>::TokenValueTooLow)?;270 let balance_to = if from != to {271 let balance_to = <AccountBalance<T>>::get((collection.id, to))272 .checked_add(1)273 .ok_or(ArithmeticError::Overflow)?;274275 ensure!(276 balance_to < collection.limits.account_token_ownership_limit(),277 <CommonError<T>>::AccountTokenLimitExceeded,278 );279280 Some(balance_to)281 } else {282 None283 };284285 // =========286287 <TokenData<T>>::insert(288 (collection.id, token),289 ItemData {290 owner: to.clone(),291 ..token_data292 },293 );294295 if let Some(balance_to) = balance_to {296 // from != to297 if balance_from == 0 {298 <AccountBalance<T>>::remove((collection.id, from));299 } else {300 <AccountBalance<T>>::insert((collection.id, from), balance_from);301 }302 <AccountBalance<T>>::insert((collection.id, to), balance_to);303 <Owned<T>>::remove((collection.id, from, token));304 <Owned<T>>::insert((collection.id, to, token), true);305 }306 Self::set_allowance_unchecked(collection, from, token, None, true);307308 collection.log_mirrored(ERC721Events::Transfer {309 from: *from.as_eth(),310 to: *to.as_eth(),311 token_id: token.into(),312 });313 <PalletCommon<T>>::deposit_event(CommonEvent::Transfer(314 collection.id,315 token,316 from.clone(),317 to.clone(),318 1,319 ));320 Ok(())321 }322323 pub fn create_multiple_items(324 collection: &NonfungibleHandle<T>,325 sender: &T::CrossAccountId,326 data: Vec<CreateItemData<T>>,327 ) -> DispatchResult {328 if !collection.is_owner_or_admin(sender) {329 ensure!(330 collection.mint_mode,331 <CommonError<T>>::PublicMintingNotAllowed332 );333 collection.check_allowlist(sender)?;334335 for item in data.iter() {336 collection.check_allowlist(&item.owner)?;337 }338 }339340 for data in data.iter() {341 <PalletCommon<T>>::ensure_correct_receiver(&data.owner)?;342 }343344 let first_token = <TokensMinted<T>>::get(collection.id);345 let tokens_minted = first_token346 .checked_add(data.len() as u32)347 .ok_or(ArithmeticError::Overflow)?;348 ensure!(349 tokens_minted <= collection.limits.token_limit(),350 <CommonError<T>>::CollectionTokenLimitExceeded351 );352353 let mut balances = BTreeMap::new();354 for data in &data {355 let balance = balances356 .entry(&data.owner)357 .or_insert_with(|| <AccountBalance<T>>::get((collection.id, &data.owner)));358 *balance = balance.checked_add(1).ok_or(ArithmeticError::Overflow)?;359360 ensure!(361 *balance <= collection.limits.account_token_ownership_limit(),362 <CommonError<T>>::AccountTokenLimitExceeded,363 );364 }365366 // =========367368 <TokensMinted<T>>::insert(collection.id, tokens_minted);369 for (account, balance) in balances {370 <AccountBalance<T>>::insert((collection.id, account), balance);371 }372 for (i, data) in data.into_iter().enumerate() {373 let token = first_token + i as u32 + 1;374375 <TokenData<T>>::insert(376 (collection.id, token),377 ItemData {378 const_data: data.const_data,379 variable_data: data.variable_data,380 owner: data.owner.clone(),381 },382 );383 <Owned<T>>::insert((collection.id, &data.owner, token), true);384385 collection.log_mirrored(ERC721Events::Transfer {386 from: H160::default(),387 to: *data.owner.as_eth(),388 token_id: token.into(),389 });390 <PalletCommon<T>>::deposit_event(CommonEvent::ItemCreated(391 collection.id,392 TokenId(token),393 data.owner.clone(),394 1,395 ));396 }397 Ok(())398 }399400 pub fn set_allowance_unchecked(401 collection: &NonfungibleHandle<T>,402 sender: &T::CrossAccountId,403 token: TokenId,404 spender: Option<&T::CrossAccountId>,405 assume_implicit_eth: bool,406 ) {407 if let Some(spender) = spender {408 let old_spender = <Allowance<T>>::get((collection.id, token));409 <Allowance<T>>::insert((collection.id, token), spender);410 // In ERC721 there is only one possible approved user of token, so we set411 // approved user to spender412 collection.log_mirrored(ERC721Events::Approval {413 owner: *sender.as_eth(),414 approved: *spender.as_eth(),415 token_id: token.into(),416 });417 // In Unique chain, any token can have any amount of approved users, so we need to418 // set allowance of old owner to 0, and allowance of new owner to 1419 if old_spender.as_ref() != Some(spender) {420 if let Some(old_owner) = old_spender {421 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(422 collection.id,423 token,424 sender.clone(),425 old_owner,426 0,427 ));428 }429 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(430 collection.id,431 token,432 sender.clone(),433 spender.clone(),434 1,435 ));436 }437 } else {438 let old_spender = <Allowance<T>>::take((collection.id, token));439 if !assume_implicit_eth {440 // In ERC721 there is only one possible approved user of token, so we set441 // approved user to zero address442 collection.log_mirrored(ERC721Events::Approval {443 owner: *sender.as_eth(),444 approved: H160::default(),445 token_id: token.into(),446 });447 }448 // In Unique chain, any token can have any amount of approved users, so we need to449 // set allowance of old owner to 0450 if let Some(old_spender) = old_spender {451 <PalletCommon<T>>::deposit_event(CommonEvent::Approved(452 collection.id,453 token,454 sender.clone(),455 old_spender,456 0,457 ));458 }459 }460 }461462 pub fn set_allowance(463 collection: &NonfungibleHandle<T>,464 sender: &T::CrossAccountId,465 token: TokenId,466 spender: Option<&T::CrossAccountId>,467 ) -> DispatchResult {468 if collection.access == AccessMode::AllowList {469 collection.check_allowlist(sender)?;470 if let Some(spender) = spender {471 collection.check_allowlist(spender)?;472 }473 }474475 if let Some(spender) = spender {476 <PalletCommon<T>>::ensure_correct_receiver(spender)?;477 }478 let token_data =479 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;480 if &token_data.owner != sender {481 ensure!(482 collection.ignores_owned_amount(sender),483 <CommonError<T>>::CantApproveMoreThanOwned484 );485 }486487 // =========488489 Self::set_allowance_unchecked(collection, sender, token, spender, false);490 Ok(())491 }492493 pub fn transfer_from(494 collection: &NonfungibleHandle<T>,495 spender: &T::CrossAccountId,496 from: &T::CrossAccountId,497 to: &T::CrossAccountId,498 token: TokenId,499 ) -> DispatchResult {500 if spender.conv_eq(from) {501 return Self::transfer(collection, from, to, token);502 }503 if collection.access == AccessMode::AllowList {504 // `from`, `to` checked in [`transfer`]505 collection.check_allowlist(spender)?;506 }507508 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {509 ensure!(510 collection.ignores_allowance(spender),511 <CommonError<T>>::ApprovedValueTooLow512 );513 }514515 // =========516517 Self::transfer(collection, from, to, token)?;518 // Allowance is reset in [`transfer`]519 Ok(())520 }521522 pub fn burn_from(523 collection: &NonfungibleHandle<T>,524 spender: &T::CrossAccountId,525 from: &T::CrossAccountId,526 token: TokenId,527 ) -> DispatchResult {528 if spender.conv_eq(from) {529 return Self::burn(collection, from, token);530 }531 if collection.access == AccessMode::AllowList {532 // `from` checked in [`burn`]533 collection.check_allowlist(spender)?;534 }535536 if <Allowance<T>>::get((collection.id, token)).as_ref() != Some(spender) {537 ensure!(538 collection.ignores_allowance(spender),539 <CommonError<T>>::ApprovedValueTooLow540 );541 }542543 // =========544545 Self::burn(collection, from, token)546 }547548 pub fn set_variable_metadata(549 collection: &NonfungibleHandle<T>,550 sender: &T::CrossAccountId,551 token: TokenId,552 data: BoundedVec<u8, CustomDataLimit>,553 ) -> DispatchResult {554 let token_data =555 <TokenData<T>>::get((collection.id, token)).ok_or(<CommonError<T>>::TokenNotFound)?;556 collection.check_can_update_meta(sender, &token_data.owner)?;557558 // =========559560 <TokenData<T>>::insert(561 (collection.id, token),562 ItemData {563 variable_data: data,564 ..token_data565 },566 );567 Ok(())568 }569570 /// Delegated to `create_multiple_items`571 pub fn create_item(572 collection: &NonfungibleHandle<T>,573 sender: &T::CrossAccountId,574 data: CreateItemData<T>,575 ) -> DispatchResult {576 Self::create_multiple_items(collection, sender, vec![data])577 }578}pallets/refungible/src/lib.rsdiffbeforeafterboth--- a/pallets/refungible/src/lib.rs
+++ b/pallets/refungible/src/lib.rs
@@ -21,9 +21,7 @@
AccessMode, CollectionId, CustomDataLimit, MAX_REFUNGIBLE_PIECES, TokenId,
CreateCollectionData, CreateRefungibleExData,
};
-use pallet_common::{
- Error as CommonError, Event as CommonEvent, Pallet as PalletCommon,
-};
+use pallet_common::{Error as CommonError, Event as CommonEvent, Pallet as PalletCommon};
use pallet_evm::account::CrossAccountId;
use sp_runtime::{ArithmeticError, DispatchError, DispatchResult};
use sp_std::{vec::Vec, vec, collections::btree_map::BTreeMap};
pallets/unique/src/eth/sponsoring.rsdiffbeforeafterboth--- a/pallets/unique/src/eth/sponsoring.rs
+++ b/pallets/unique/src/eth/sponsoring.rs
@@ -32,7 +32,9 @@
use up_data_structs::{CreateItemData, CreateNftData};
pub struct UniqueEthSponsorshipHandler<T: Config>(PhantomData<*const T>);
-impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)> for UniqueEthSponsorshipHandler<T> {
+impl<T: Config> SponsorshipHandler<T::CrossAccountId, (H160, Vec<u8>)>
+ for UniqueEthSponsorshipHandler<T>
+{
fn get_sponsor(who: &T::CrossAccountId, call: &(H160, Vec<u8>)) -> Option<T::CrossAccountId> {
let collection_id = map_eth_to_id(&call.0)?;
let collection = <CollectionHandle<T>>::new(collection_id)?;
@@ -59,16 +61,17 @@
.map(|()| sponsor)
}
UniqueNFTCall::ERC721Mintable(call) => match call {
- pallet_nonfungible::erc::ERC721MintableCall::Mint { .. } |
- pallet_nonfungible::erc::ERC721MintableCall::MintWithTokenUri { .. } => {
- withdraw_create_item(
- &collection,
- who.as_sub(),
- &CreateItemData::NFT(CreateNftData::default()))
- .map(|()| sponsor)
- }
+ pallet_nonfungible::erc::ERC721MintableCall::Mint { .. }
+ | pallet_nonfungible::erc::ERC721MintableCall::MintWithTokenUri {
+ ..
+ } => withdraw_create_item(
+ &collection,
+ who.as_sub(),
+ &CreateItemData::NFT(CreateNftData::default()),
+ )
+ .map(|()| sponsor),
_ => None,
- }
+ },
_ => None,
}
}
pallets/unique/src/lib.rsdiffbeforeafterboth--- a/pallets/unique/src/lib.rs
+++ b/pallets/unique/src/lib.rs
@@ -53,10 +53,7 @@
CollectionMode, TokenId, SchemaVersion, SponsorshipState, MetaUpdatePermission,
CreateCollectionData, CustomDataLimit, CreateItemExData,
};
-use pallet_common::{
- CollectionHandle, Pallet as PalletCommon, Error as CommonError,
- CommonWeightInfo,
-};
+use pallet_common::{CollectionHandle, Pallet as PalletCommon, Error as CommonError, CommonWeightInfo};
use pallet_evm::account::CrossAccountId;
use pallet_refungible::{Pallet as PalletRefungible, RefungibleHandle};
use pallet_fungible::{Pallet as PalletFungible, FungibleHandle};
pallets/unique/src/tests.rsdiffbeforeafterboth--- a/pallets/unique/src/tests.rs
+++ b/pallets/unique/src/tests.rs
@@ -2865,18 +2865,49 @@
let origin1 = Origin::signed(user1);
let origin2 = Origin::signed(user2);
let account2 = account(user2);
-
- let collection_id = create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
- assert_ok!(TemplateModule::set_collection_sponsor(origin1.clone(), collection_id, user1));
- assert_ok!(TemplateModule::confirm_sponsorship(origin1.clone(), collection_id));
+ let collection_id =
+ create_test_collection_for_owner(&CollectionMode::NFT, user1, CollectionId(1));
+ assert_ok!(TemplateModule::set_collection_sponsor(
+ origin1.clone(),
+ collection_id,
+ user1
+ ));
+ assert_ok!(TemplateModule::confirm_sponsorship(
+ origin1.clone(),
+ collection_id
+ ));
+
// Expect error while have no permissions
- assert!(TemplateModule::create_item(origin2.clone(), collection_id, account2.clone(), default_nft_data().into()).is_err());
+ assert!(TemplateModule::create_item(
+ origin2.clone(),
+ collection_id,
+ account2.clone(),
+ default_nft_data().into()
+ )
+ .is_err());
- assert_ok!(TemplateModule::set_public_access_mode(origin1.clone(), collection_id, AccessMode::AllowList));
- assert_ok!(TemplateModule::add_to_allow_list(origin1.clone(), collection_id, account2.clone()));
- assert_ok!(TemplateModule::set_mint_permission(origin1.clone(), collection_id, true));
-
- assert_ok!(TemplateModule::create_item(origin2, collection_id, account2, default_nft_data().into()));
+ assert_ok!(TemplateModule::set_public_access_mode(
+ origin1.clone(),
+ collection_id,
+ AccessMode::AllowList
+ ));
+ assert_ok!(TemplateModule::add_to_allow_list(
+ origin1.clone(),
+ collection_id,
+ account2.clone()
+ ));
+ assert_ok!(TemplateModule::set_mint_permission(
+ origin1.clone(),
+ collection_id,
+ true
+ ));
+
+ assert_ok!(TemplateModule::create_item(
+ origin2,
+ collection_id,
+ account2,
+ default_nft_data().into()
+ ));
});
-}
\ No newline at end of file
+}