difftreelog
refactor rewrite nft pallet to evm helpers
in: master
7 files changed
pallets/nft/Cargo.tomldiffbeforeafterboth393940 'primitive-types/std',40 'primitive-types/std',41 'evm-coder/std',41 'evm-coder/std',42 'evm-coder-substrate/std',42 'pallet-evm-coder-substrate/std',43]43]444445################################################################################45################################################################################144sp-api = { default-features = false, version = '3.0.0', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.8" }144sp-api = { default-features = false, version = '3.0.0', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.8" }145145146evm-coder = { default-features = false, path = "../../crates/evm-coder" }146evm-coder = { default-features = false, path = "../../crates/evm-coder" }147evm-coder-substrate = { default-features = false, path = "../../crates/evm-coder-substrate" }147pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" }148primitive-types = { version = "0.9.0", default-features = false, features = ["serde_no_std"] }148primitive-types = { version = "0.9.0", default-features = false, features = [149 "serde_no_std",150] }pallets/nft/src/eth/erc.rsdiffbeforeafterboth1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};1use evm_coder::{solidity_interface, solidity, types::*, ToLog};2use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*};3use core::convert::TryInto;4use alloc::format;5use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};6use frame_support::storage::StorageDoubleMap;7use pallet_evm::AddressMapping;8use super::account::CrossAccountId;2use sp_std::vec::Vec;9use sp_std::vec::Vec;3104#[solidity_interface]11#[solidity_interface(name = "ERC165")]5pub trait InlineNameSymbol {6 type Error;78 fn name(&self) -> Result<string, Self::Error>;12impl<T: Config> CollectionHandle<T> {9 fn symbol(&self) -> Result<string, Self::Error>;13 fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {10}14 Ok(match self.mode {15 CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),16 CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),17 _ => false,18 })19 }20}112112#[solidity_interface]22#[solidity_interface(name = "InlineNameSymbol")]13pub trait InlineTotalSupply {23impl<T: Config> CollectionHandle<T> {14 type Error;24 fn name(&self) -> Result<string> {25 Ok(decode_utf16(self.name.iter().copied())26 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))27 .collect::<string>())28 }152916 fn total_supply(&self) -> Result<uint256, Self::Error>;30 fn symbol(&self) -> Result<string> {31 Ok(string::from_utf8_lossy(&self.token_prefix).into())32 }17}33}183419#[solidity_interface]35#[solidity_interface(name = "InlineTotalSupply")]20pub trait ERC165 {36impl<T: Config> CollectionHandle<T> {21 type Error;2223 fn supports_interface(&self, interface_id: bytes4) -> Result<bool, Self::Error>;37 fn total_supply(&self) -> Result<uint256> {24}38 // TODO: we do not track total amount of all tokens39 Ok(0.into())40 }41}254226#[solidity_interface(inline_is(InlineNameSymbol))]43#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]27pub trait ERC721Metadata {44impl<T: Config> CollectionHandle<T> {28 type Error;2930 #[solidity(rename_selector = "tokenURI")]31 fn token_uri(&self, token_id: uint256) -> Result<string, Self::Error>;45 fn token_uri(&self, token_id: uint256) -> Result<string> {32}46 // TODO: We should standartize url prefix, maybe via offchain schema?47 Ok(format!("unique.network/{}/{}", self.id, token_id))48 }49}335034#[solidity_interface(inline_is(InlineTotalSupply))]51#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))]35pub trait ERC721Enumerable {52impl<T: Config> CollectionHandle<T> {36 type Error;3738 fn token_by_index(&self, index: uint256) -> Result<uint256, Self::Error>;53 fn token_by_index(&self, index: uint256) -> Result<uint256> {54 Ok(index)55 }5639 fn token_of_owner_by_index(57 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {40 &self,58 // TODO: Not implemetable41 owner: address,59 Err("not implemented".into())42 index: uint256,60 }43 ) -> Result<uint256, Self::Error>;61}44}456246#[derive(ToLog)]63#[derive(ToLog)]47pub enum ERC721Events {64pub enum ERC721Events {71 },88 },72}89}739074#[solidity_interface(is(ERC165), events(ERC721Events))]91#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]75pub trait ERC721 {92impl<T: Config> CollectionHandle<T> {76 type Error;93 #[solidity(rename_selector = "balanceOf")]7778 fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;94 fn balance_of_nft(&self, owner: address) -> Result<uint256> {95 let owner = T::EvmAddressMapping::into_account_id(owner);96 let balance = <Balance<T>>::get(self.id, owner);97 Ok(balance.into())98 }79 fn owner_of(&self, token_id: uint256) -> Result<address, Self::Error>;99 fn owner_of(&self, token_id: uint256) -> Result<address> {80100 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;81 #[solidity(rename_selector = "safeTransferFrom")]101 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;102 Ok(*token.owner.as_eth())103 }82 fn safe_transfer_from_with_data(104 fn safe_transfer_from_with_data(83 &mut self,105 &mut self,84 from: address,106 _from: address,85 to: address,107 _to: address,86 token_id: uint256,108 _token_id: uint256,87 data: bytes,109 _data: bytes,88 value: value,110 _value: value,89 ) -> Result<void, Self::Error>;111 ) -> Result<void> {112 // TODO: Not implemetable113 Err("not implemented".into())114 }90 fn safe_transfer_from(115 fn safe_transfer_from(91 &mut self,116 &mut self,92 from: address,117 _from: address,93 to: address,118 _to: address,94 token_id: uint256,119 _token_id: uint256,95 value: value,120 _value: value,96 ) -> Result<void, Self::Error>;121 ) -> Result<void> {122 // TODO: Not implemetable123 Err("not implemented".into())124 }9712598 fn transfer_from(126 fn transfer_from(99 &mut self,127 &mut self,100 caller: caller,128 caller: caller,101 from: address,129 from: address,102 to: address,130 to: address,103 token_id: uint256,131 token_id: uint256,104 value: value,132 _value: value,105 ) -> Result<void, Self::Error>;133 ) -> Result<void> {134 let caller = T::CrossAccountId::from_eth(caller);135 let from = T::CrossAccountId::from_eth(from);136 let to = T::CrossAccountId::from_eth(to);137 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;138139 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)140 .map_err(|_| "transferFrom error")?;141 Ok(())142 }143106 fn approve(144 fn approve(107 &mut self,145 &mut self,108 caller: caller,146 caller: caller,109 approved: address,147 approved: address,110 token_id: uint256,148 token_id: uint256,111 value: value,149 _value: value,112 ) -> Result<void, Self::Error>;150 ) -> Result<void> {151 let caller = T::CrossAccountId::from_eth(caller);152 let approved = T::CrossAccountId::from_eth(approved);153 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;154155 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)156 .map_err(|_| "approve internal")?;157 Ok(())158 }159113 fn set_approval_for_all(160 fn set_approval_for_all(114 &mut self,161 &mut self,115 caller: caller,162 _caller: caller,116 operator: address,163 _operator: address,117 approved: bool,164 _approved: bool,118 ) -> Result<void, Self::Error>;165 ) -> Result<void> {166 // TODO: Not implemetable167 Err("not implemented".into())168 }119169120 fn get_approved(&self, token_id: uint256) -> Result<address, Self::Error>;170 fn get_approved(&self, _token_id: uint256) -> Result<address> {171 // TODO: Not implemetable172 Err("not implemented".into())173 }174121 fn is_approved_for_all(175 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {122 &self,176 // TODO: Not implemetable123 owner: address,177 Err("not implemented".into())124 operator: address,178 }125 ) -> Result<address, Self::Error>;126}179}127180128#[solidity_interface]181#[solidity_interface(name = "ERC721UniqueExtensions")]129pub trait ERC721UniqueExtensions {182impl<T: Config> CollectionHandle<T> {130 type Error;183 #[solidity(rename_selector = "transfer")]131132 fn transfer(184 fn transfer_nft(133 &mut self,185 &mut self,134 caller: caller,186 caller: caller,135 to: address,187 to: address,136 token_id: uint256,188 token_id: uint256,137 value: value,189 _value: value,138 ) -> Result<void, Self::Error>;190 ) -> Result<void> {191 let caller = T::CrossAccountId::from_eth(caller);192 let to = T::CrossAccountId::from_eth(to);193 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;194195 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)196 .map_err(|_| "transfer error")?;197 Ok(())198 }139}199}140200141#[solidity_interface(is(201#[solidity_interface(202 name = "UniqueNFT",203 is(142 ERC165,204 ERC165,143 ERC721,205 ERC721,144 ERC721Metadata,206 ERC721Metadata,145 ERC721Enumerable,207 ERC721Enumerable,146 ERC721UniqueExtensions208 ERC721UniqueExtensions147))]209 )148pub trait UniqueNFT {210)]149 type Error;211impl<T: Config> CollectionHandle<T> {}150}151212152#[derive(ToLog)]213#[derive(ToLog)]153pub enum ERC20Events {214pub enum ERC20Events {168}229}169230170#[solidity_interface(inline_is(InlineNameSymbol, InlineTotalSupply), events(ERC20Events))]231#[solidity_interface(171pub trait ERC20 {232 name = "ERC20",172 type Error;233 inline_is(InlineNameSymbol, InlineTotalSupply),173234 events(ERC20Events)235)]236impl<T: Config> CollectionHandle<T> {174 fn decimals(&self) -> Result<uint8, Self::Error>;237 fn decimals(&self) -> Result<uint8> {238 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {239 *decimals240 } else {241 unreachable!()242 })243 }175 fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;244 fn balance_of(&self, owner: address) -> Result<uint256> {245 let owner = T::EvmAddressMapping::into_account_id(owner);246 let balance = <Balance<T>>::get(self.id, owner);247 Ok(balance.into())248 }176 fn transfer(249 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {177 &mut self,178 caller: caller,179 to: address,180 value: uint256,181 ) -> Result<bool, Self::Error>;250 let caller = T::CrossAccountId::from_eth(caller);251 let to = T::CrossAccountId::from_eth(to);252 let amount = amount.try_into().map_err(|_| "amount overflow")?;253254 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)255 .map_err(|_| "transfer error")?;256 Ok(true)257 }258 #[solidity(rename_selector = "transferFrom")]182 fn transfer_from(259 fn transfer_from_fungible(183 &mut self,260 &mut self,184 caller: caller,261 caller: caller,185 from: address,262 from: address,186 to: address,263 to: address,187 value: uint256,264 amount: uint256,188 ) -> Result<bool, Self::Error>;265 ) -> Result<bool> {266 let caller = T::CrossAccountId::from_eth(caller);267 let from = T::CrossAccountId::from_eth(from);268 let to = T::CrossAccountId::from_eth(to);269 let amount = amount.try_into().map_err(|_| "amount overflow")?;270271 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)272 .map_err(|_| "transferFrom error")?;273 Ok(true)274 }275 #[solidity(rename_selector = "approve")]189 fn approve(276 fn approve_fungible(190 &mut self,277 &mut self,191 caller: caller,278 caller: caller,192 spender: address,279 spender: address,193 value: uint256,280 amount: uint256,194 ) -> Result<bool, Self::Error>;281 ) -> Result<bool> {282 let caller = T::CrossAccountId::from_eth(caller);283 let spender = T::CrossAccountId::from_eth(spender);284 let amount = amount.try_into().map_err(|_| "amount overflow")?;285286 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)287 .map_err(|_| "approve internal")?;288 Ok(true)289 }195 fn allowance(&self, owner: address, spender: address) -> Result<uint256, Self::Error>;290 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {196}291 let owner = T::CrossAccountId::from_eth(owner);292 let spender = T::CrossAccountId::from_eth(spender);293294 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())295 }296}197297198#[solidity_interface(is(ERC165, ERC20))]298#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]199pub trait UniqueFungible {299impl<T: Config> CollectionHandle<T> {}200 type Error;201}202300pallets/nft/src/eth/erc_impl.rsdiffbeforeafterbothno changes
pallets/nft/src/eth/log.rsdiffbeforeafterbothno changes
pallets/nft/src/eth/mod.rsdiffbeforeafterboth1pub mod account;1pub mod account;2pub mod erc;2pub mod erc;3mod erc_impl;4pub mod log;5pub mod sponsoring;3pub mod sponsoring;647use evm_coder::abi::AbiWriter;5use pallet_evm_coder_substrate::call_internal;8use evm_coder::abi::StringError;9use sp_std::prelude::*;10use sp_std::borrow::ToOwned;6use sp_std::borrow::ToOwned;11use sp_std::vec::Vec;7use sp_std::vec::Vec;1213use pallet_evm::{PrecompileOutput, ExitReason, ExitRevert, ExitSucceed};8use pallet_evm::{PrecompileOutput};14use sp_core::{H160, U256};9use sp_core::{H160, U256};15use frame_support::storage::StorageMap;10use frame_support::storage::StorageMap;1617use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};11use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode};1819use erc::{UniqueFungible, UniqueFungibleCall, UniqueNFT, UniqueNFTCall};12use erc::{UniqueFungibleCall, UniqueNFTCall};20use evm_coder::{types::*, abi::AbiReader};211322pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);14pub struct NftErcSupport<T: Config>(core::marker::PhantomData<T>);231542 H160(out)34 H160(out)43}35}443645fn call_internal<T: Config>(37/*46 collection: &mut CollectionHandle<T>,38fn call_internal<T: Config>(47 caller: caller,39 collection: &mut CollectionHandle<T>,48 method_id: u32,40 caller: caller,49 mut input: AbiReader,41 method_id: u32,50 value: U256,42 mut input: AbiReader,51) -> Result<Option<AbiWriter>, evm_coder::abi::StringError> {43 value: U256,52 match collection.mode.clone() {44) -> Result<Option<AbiWriter>> {53 CollectionMode::Fungible(_) => {45 match collection.mode.clone() {54 #[cfg(feature = "std")]46 CollectionMode::Fungible(_) => {55 {47 call_internal();56 println!("Parse fungible call {:x}", method_id);48 let call = match UniqueFungibleCall::parse(method_id, &mut input)? {57 }49 Some(v) => v,58 let call = match UniqueFungibleCall::parse(method_id, &mut input)? {50 None => {59 Some(v) => v,51 #[cfg(feature = "std")]60 None => {52 {61 #[cfg(feature = "std")]53 println!("Method not found");62 {54 }63 println!("Method not found");55 return Ok(None);64 }56 }65 return Ok(None);57 };66 }58 Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(67 };59 collection,68 #[cfg(feature = "std")]60 Msg {69 {61 call,70 dbg!(&call);62 caller,71 }63 value,72 Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(64 },73 collection,65 )?))74 Msg {66 }75 call,67 CollectionMode::NFT => {76 caller,68 let call = match UniqueNFTCall::parse(method_id, &mut input)? {77 value,69 Some(v) => v,78 },70 None => return Ok(None),79 )?))71 };80 }72 Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(81 CollectionMode::NFT => {73 collection,82 let call = match UniqueNFTCall::parse(method_id, &mut input)? {74 Msg {83 Some(v) => v,75 call,84 None => return Ok(None),76 caller,85 };77 value,86 Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(78 },87 collection,79 )?))88 Msg {80 }89 call,81 _ => Err("erc calls only supported to fungible and nft collections for now".into()),90 caller,82 }91 value,83}*/92 },93 )?))94 }95 _ => Err(StringError::from(96 "erc calls only supported to fungible and nft collections for now",97 )),98 }99}10084101impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {85impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {102 fn is_reserved(target: &H160) -> bool {86 fn is_reserved(target: &H160) -> bool {129 ) -> Option<PrecompileOutput> {113 ) -> Option<PrecompileOutput> {130 let mut collection = map_eth_to_id(target)114 let mut collection = map_eth_to_id(target)131 .and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;115 .and_then(|id| <CollectionHandle<T>>::get_with_gas_limit(id, gas_limit))?;132 let (method_id, input) = AbiReader::new_call(input).unwrap();133 let result = call_internal(&mut collection, *source, method_id, input, value);116 let result = match collection.mode {134 let cost = gas_limit - collection.gas_left();117 CollectionMode::NFT => {118 call_internal::<UniqueNFTCall, _>(*source, &mut collection, value, input)119 }120 CollectionMode::Fungible(_) => {121 call_internal::<UniqueFungibleCall, _>(*source, &mut collection, value, input)122 }123 _ => return None,124 };135 let logs = collection.logs.retrieve_logs();125 collection.recorder.evm_to_precompile_output(result)136 match result {137 Ok(Some(v)) => Some(PrecompileOutput {138 exit_status: ExitReason::Succeed(ExitSucceed::Returned),139 cost,140 logs,141 output: v.finish(),142 }),143 Ok(None) => None,144 Err(e) => Some(PrecompileOutput {145 exit_status: ExitReason::Revert(ExitRevert::Reverted),146 cost: 0,147 logs: Default::default(),148 output: AbiWriter::from(e).finish(),149 }),150 }151 }126 }152}127}153154// TODO: This function is slow, and output can be memoized155pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {156 // FIXME: Can be done on wasm runtime with https://github.com/paritytech/substrate/pull/8728157 #[cfg(feature = "std")]158 {159 let contract = collection_id_to_address(collection_id);160 let signed = ethereum_tx_sign::RawTransaction {161 nonce: 0.into(),162 to: Some(contract.0.into()),163 value: 0.into(),164 gas_price: 0.into(),165 gas: 0.into(),166 // zero selector, this transaction always have same sender, so all data should be acquired from logs167 data: Vec::from([0, 0, 0, 0]),168 }169 .sign(170 // TODO: move to pallet config171 // 0xF70631E55faff9f3FD3681545aa6c724226a3853172 // 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a173 &hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a")174 .into(),175 &chain_id,176 );177 rlp::decode::<ethereum::Transaction>(&signed)178 .expect("transaction is just created, it can't be broken")179 }180 #[cfg(not(feature = "std"))]181 {182 panic!("transaction generation not yet supported by wasm runtime while generating transaction for collection_id {}, chain_id {}", collection_id, chain_id)183 }184}185128pallets/nft/src/lib.rsdiffbeforeafterboth36use sp_std::vec;36use sp_std::vec;37use sp_runtime::sp_std::prelude::Vec;37use sp_runtime::sp_std::prelude::Vec;38use core::ops::{Deref, DerefMut};38use core::ops::{Deref, DerefMut};39use core::cell::RefCell;40use nft_data_structs::{39use nft_data_structs::{41 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,40 MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES,42 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,41 AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId,43 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,42 CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType,44 FungibleItemType, ReFungibleItemType,43 FungibleItemType, ReFungibleItemType,45};44};46use pallet_ethereum::EthereumTransactionSender;474548#[cfg(test)]46#[cfg(test)]49mod mock;47mod mock;55mod eth;53mod eth;56mod sponsorship;54mod sponsorship;57pub use sponsorship::NftSponsorshipHandler;55pub use sponsorship::NftSponsorshipHandler;56pub use eth::sponsoring::NftEthSponsorshipHandler;585759pub use eth::NftErcSupport;58pub use eth::NftErcSupport;60pub use eth::account::*;59pub use eth::account::*;178 }177 }179}178}180179180#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"]181pub struct CollectionHandle<T: Config> {181pub struct CollectionHandle<T: Config> {182 pub id: CollectionId,182 pub id: CollectionId,183 collection: Collection<T>,183 collection: Collection<T>,184 logs: eth::log::LogRecorder,184 recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,185 evm_address: H160,186 gas_limit: RefCell<u64>,187}185}188impl<T: Config> CollectionHandle<T> {186impl<T: Config> CollectionHandle<T> {189 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {187 pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {190 <CollectionById<T>>::get(id).map(|collection| Self {188 <CollectionById<T>>::get(id).map(|collection| Self {191 id,189 id,192 collection,190 collection,193 logs: eth::log::LogRecorder::default(),194 evm_address: eth::collection_id_to_address(id),191 recorder: pallet_evm_coder_substrate::SubstrateRecorder::new(195 gas_limit: RefCell::new(gas_limit),192 eth::collection_id_to_address(id),193 gas_limit,194 ),196 })195 })197 }196 }198 pub fn get(id: CollectionId) -> Option<Self> {197 pub fn get(id: CollectionId) -> Option<Self> {199 Self::get_with_gas_limit(id, u64::MAX)198 Self::get_with_gas_limit(id, u64::MAX)200 }199 }201 pub fn gas_left(&self) -> u64 {200 pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult {202 *self.gas_limit.borrow()201 self.recorder.log_sub(log)203 }202 }204 pub fn consume_gas(&self, gas: u64) -> DispatchResult {203 fn consume_gas(&self, gas: u64) -> DispatchResult {205 let mut gas_limit = self.gas_limit.borrow_mut();204 self.recorder.consume_gas_sub(gas)206 if *gas_limit < gas {207 fail!(Error::<T>::OutOfGas);208 }209 *gas_limit -= gas;210 Ok(())211 }205 }212 pub fn log(&self, log: impl evm_coder::ToLog) {206 pub fn submit_logs(self) -> DispatchResult {213 self.logs.log(log.to_log(self.evm_address))207 self.recorder.submit_logs()214 }208 }215 pub fn into_inner(self) -> Collection<T> {209 pub fn save(self) -> DispatchResult {210 self.recorder.submit_logs()?;216 self.collection211 <CollectionById<T>>::insert(self.id, self.collection);217 }212 Ok(())213 }218}214}219impl<T: Config> Deref for CollectionHandle<T> {215impl<T: Config> Deref for CollectionHandle<T> {220 type Target = Collection<T>;216 type Target = Collection<T>;230 }226 }231}227}232228233pub trait Config: system::Config + Sized {229pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {234 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;230 type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;235231236 /// Weight information for extrinsics in this pallet.232 /// Weight information for extrinsics in this pallet.246 >;242 >;247 type TreasuryAccountId: Get<Self::AccountId>;243 type TreasuryAccountId: Get<Self::AccountId>;248249 type EthereumChainId: Get<u64>;250 type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;251}244}252245253// # Used definitions246// # Used definitions564 fail!(Error::<T>::NoPermission);557 fail!(Error::<T>::NoPermission);565 }558 }566559567 <AddressTokens<T>>::remove_prefix(collection_id);560 <AddressTokens<T>>::remove_prefix(collection_id, None);568 <Allowances<T>>::remove_prefix(collection_id);561 <Allowances<T>>::remove_prefix(collection_id, None);569 <Balance<T>>::remove_prefix(collection_id);562 <Balance<T>>::remove_prefix(collection_id, None);570 <ItemListIndex>::remove(collection_id);563 <ItemListIndex>::remove(collection_id);571 <AdminList<T>>::remove(collection_id);564 <AdminList<T>>::remove(collection_id);572 <CollectionById<T>>::remove(collection_id);565 <CollectionById<T>>::remove(collection_id);573 <WhiteList<T>>::remove_prefix(collection_id);566 <WhiteList<T>>::remove_prefix(collection_id, None);574567575 <NftItemList<T>>::remove_prefix(collection_id);568 <NftItemList<T>>::remove_prefix(collection_id, None);576 <FungibleItemList<T>>::remove_prefix(collection_id);569 <FungibleItemList<T>>::remove_prefix(collection_id, None);577 <ReFungibleItemList<T>>::remove_prefix(collection_id);570 <ReFungibleItemList<T>>::remove_prefix(collection_id, None);578571579 <NftTransferBasket<T>>::remove_prefix(collection_id);572 <NftTransferBasket<T>>::remove_prefix(collection_id, None);580 <FungibleTransferBasket<T>>::remove_prefix(collection_id);573 <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);581 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);574 <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);582575583 <VariableMetaDataBasket<T>>::remove_prefix(collection_id);576 <VariableMetaDataBasket<T>>::remove_prefix(collection_id, None);584577585 DestroyedCollectionCount::put(DestroyedCollectionCount::get()578 DestroyedCollectionCount::put(DestroyedCollectionCount::get()586 .checked_add(1)579 .checked_add(1)667 let mut target_collection = Self::get_collection(collection_id)?;660 let mut target_collection = Self::get_collection(collection_id)?;668 Self::check_owner_permissions(&target_collection, &sender)?;661 Self::check_owner_permissions(&target_collection, &sender)?;669 target_collection.access = mode;662 target_collection.access = mode;670 Self::save_collection(target_collection);663 target_collection.save()671672 Ok(())673 }664 }674665675 /// Allows Anyone to create tokens if:666 /// Allows Anyone to create tokens if:694 let mut target_collection = Self::get_collection(collection_id)?;685 let mut target_collection = Self::get_collection(collection_id)?;695 Self::check_owner_permissions(&target_collection, &sender)?;686 Self::check_owner_permissions(&target_collection, &sender)?;696 target_collection.mint_mode = mint_permission;687 target_collection.mint_mode = mint_permission;697 Self::save_collection(target_collection);688 target_collection.save()698699 Ok(())700 }689 }701690702 /// Change the owner of the collection.691 /// Change the owner of the collection.718 let mut target_collection = Self::get_collection(collection_id)?;707 let mut target_collection = Self::get_collection(collection_id)?;719 Self::check_owner_permissions(&target_collection, &sender)?;708 Self::check_owner_permissions(&target_collection, &sender)?;720 target_collection.owner = new_owner;709 target_collection.owner = new_owner;721 Self::save_collection(target_collection);710 target_collection.save()722723 Ok(())724 }711 }725712726 /// Adds an admin of the Collection.713 /// Adds an admin of the Collection.800 Self::check_owner_permissions(&target_collection, &sender)?;787 Self::check_owner_permissions(&target_collection, &sender)?;801788802 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);789 target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor);803 Self::save_collection(target_collection);790 target_collection.save()804805 Ok(())806 }791 }807792808 /// # Permissions793 /// # Permissions824 );809 );825810826 target_collection.sponsorship = SponsorshipState::Confirmed(sender);811 target_collection.sponsorship = SponsorshipState::Confirmed(sender);827 Self::save_collection(target_collection);812 target_collection.save()828829 Ok(())830 }813 }831814832 /// Switch back to pay-per-own-transaction model.815 /// Switch back to pay-per-own-transaction model.847 Self::check_owner_permissions(&target_collection, &sender)?;830 Self::check_owner_permissions(&target_collection, &sender)?;848831849 target_collection.sponsorship = SponsorshipState::Disabled;832 target_collection.sponsorship = SponsorshipState::Disabled;850 Self::save_collection(target_collection);833 target_collection.save()851852 Ok(())853 }834 }854835855 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.836 /// This method creates a concrete instance of NFT Collection created with CreateCollection method.884865885 Self::create_item_internal(&sender, &collection, &owner, data)?;866 Self::create_item_internal(&sender, &collection, &owner, data)?;886867887 Self::submit_logs(collection)?;868 collection.submit_logs()888 Ok(())889 }869 }890870891 /// This method creates multiple items in a collection created with CreateCollection method.871 /// This method creates multiple items in a collection created with CreateCollection method.918898919 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;899 Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?;920900921 Self::submit_logs(collection)?;901 collection.submit_logs()922 Ok(())923 }902 }924903925 /// Destroys a concrete instance of NFT.904 /// Destroys a concrete instance of NFT.944923945 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;924 Self::burn_item_internal(&sender, &target_collection, item_id, value)?;946925947 Self::submit_logs(target_collection)?;926 target_collection.submit_logs()948 Ok(())949 }927 }950928951 /// Change ownership of the token.929 /// Change ownership of the token.979957980 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;958 Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?;981959982 Self::submit_logs(collection)?;960 collection.submit_logs()983 Ok(())984 }961 }985962986 /// Set, change, or remove approved address to transfer the ownership of the NFT.963 /// Set, change, or remove approved address to transfer the ownership of the NFT.10069831007 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;984 Self::approve_internal(&sender, &spender, &collection, item_id, amount)?;10089851009 Self::submit_logs(collection)?;986 collection.submit_logs()1010 Ok(())1011 }987 }10129881013 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.989 /// Change ownership of a NFT on behalf of the owner. See Approve method for additional information. After this method executes, the approval is removed so that the approved address will not be able to transfer this NFT again from this owner.103710131038 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;1014 Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?;103910151040 Self::submit_logs(collection)?;1016 collection.submit_logs()1041 Ok(())1042 }1017 }1043 // #[weight = 0]1018 // #[weight = 0]1044 // // let no_perm_mes = "You do not have permissions to modify this collection";1019 // // let no_perm_mes = "You do not have permissions to modify this collection";1107 let mut target_collection = Self::get_collection(collection_id)?;1082 let mut target_collection = Self::get_collection(collection_id)?;1108 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1083 Self::check_owner_or_admin_permissions(&target_collection, &sender)?;1109 target_collection.schema_version = version;1084 target_collection.schema_version = version;1110 Self::save_collection(target_collection);1085 target_collection.save()11111112 Ok(())1113 }1086 }111410871115 /// Set off-chain data schema.1088 /// Set off-chain data schema.1139 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");1112 ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, "");114011131141 target_collection.offchain_schema = schema;1114 target_collection.offchain_schema = schema;1142 Self::save_collection(target_collection);1115 target_collection.save()11431144 Ok(())1145 }1116 }114611171147 /// Set const on-chain data schema.1118 /// Set const on-chain data schema.1171 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");1142 ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, "");117211431173 target_collection.const_on_chain_schema = schema;1144 target_collection.const_on_chain_schema = schema;1174 Self::save_collection(target_collection);1145 target_collection.save()11751176 Ok(())1177 }1146 }117811471179 /// Set variable on-chain data schema.1148 /// Set variable on-chain data schema.1203 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");1172 ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, "");120411731205 target_collection.variable_on_chain_schema = schema;1174 target_collection.variable_on_chain_schema = schema;1206 Self::save_collection(target_collection);1175 target_collection.save()12071208 Ok(())1209 }1176 }121011771211 // Sudo permissions function1178 // Sudo permissions function125412211255 target_collection.limits = new_limits;1222 target_collection.limits = new_limits;12231256 Self::save_collection(target_collection);1224 target_collection.save()12571258 Ok(())1259 }1225 }1260 }1226 }1261}1227}1376 owner: *sender.as_eth(),1342 owner: *sender.as_eth(),1377 approved: *spender.as_eth(),1343 approved: *spender.as_eth(),1378 token_id: item_id.into(),1344 token_id: item_id.into(),1379 });1345 })?;1380 }1346 }138113471382 if matches!(collection.mode, CollectionMode::Fungible(_)) {1348 if matches!(collection.mode, CollectionMode::Fungible(_)) {1385 owner: *sender.as_eth(),1351 owner: *sender.as_eth(),1386 spender: *spender.as_eth(),1352 spender: *spender.as_eth(),1387 value: allowance.into(),1353 value: allowance.into(),1388 });1354 })?;1389 }1355 }139013561391 Self::deposit_event(RawEvent::Approved(1357 Self::deposit_event(RawEvent::Approved(1461 owner: *from.as_eth(),1427 owner: *from.as_eth(),1462 spender: *sender.as_eth(),1428 spender: *sender.as_eth(),1463 value: allowance.into(),1429 value: allowance.into(),1464 });1430 })?;1465 }1431 }146614321467 Ok(())1433 Ok(())1789 from: H160::default(),1755 from: H160::default(),1790 to: *item_owner.as_eth(),1756 to: *item_owner.as_eth(),1791 token_id: current_index.into(),1757 token_id: current_index.into(),1792 });1758 })?;1793 Self::deposit_event(RawEvent::ItemCreated(1759 Self::deposit_event(RawEvent::ItemCreated(1794 collection_id,1760 collection_id,1795 current_index,1761 current_index,1886 from: *owner.as_eth(),1852 from: *owner.as_eth(),1887 to: H160::default(),1853 to: H160::default(),1888 value: value.into(),1854 value: value.into(),1889 });1855 })?;1890 Ok(())1856 Ok(())1891 }1857 }189218581896 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1862 Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)1897 }1863 }18981899 fn save_collection(collection: CollectionHandle<T>) {1900 <CollectionById<T>>::insert(collection.id, collection.into_inner());1901 }19021903 pub fn submit_logs(collection: CollectionHandle<T>) -> DispatchResult {1904 if collection.logs.is_empty() {1905 return Ok(());1906 }1907 T::EthereumTransactionSender::submit_logs_transaction(1908 eth::generate_transaction(collection.id, T::EthereumChainId::get()),1909 collection.logs.retrieve_logs(),1910 )1911 }191218641913 fn check_owner_permissions(1865 fn check_owner_permissions(1914 target_collection: &CollectionHandle<T>,1866 target_collection: &CollectionHandle<T>,2037 from: *owner.as_eth(),1989 from: *owner.as_eth(),2038 to: *recipient.as_eth(),1990 to: *recipient.as_eth(),2039 value: value.into(),1991 value: value.into(),2040 });1992 })?;2041 Self::deposit_event(RawEvent::Transfer(1993 Self::deposit_event(RawEvent::Transfer(2042 collection.id,1994 collection.id,2043 1,1995 1,2173 from: *sender.as_eth(),2125 from: *sender.as_eth(),2174 to: *new_owner.as_eth(),2126 to: *new_owner.as_eth(),2175 token_id: item_id.into(),2127 token_id: item_id.into(),2176 });2128 })?;2177 Self::deposit_event(RawEvent::Transfer(2129 Self::deposit_event(RawEvent::Transfer(2178 collection.id,2130 collection.id,2179 item_id,2131 item_id,pallets/nft/src/mock.rsdiffbeforeafterboth193 type EvmAddressMapping = TestEvmAddressMapping;193 type EvmAddressMapping = TestEvmAddressMapping;194 type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;194 type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping;195 type CrossAccountId = TestCrossAccountId;195 type CrossAccountId = TestCrossAccountId;196 type EthereumChainId = EthereumChainId;197 type EthereumTransactionSender = TestEtheremTransactionSender;196 type EthereumTransactionSender = TestEtheremTransactionSender;198}197}199198