From 5fc39c3d8a705e1acebcc1974deda14ac80deeb0 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Tue, 27 Jul 2021 16:36:05 +0000 Subject: [PATCH] refactor: rewrite nft pallet to evm helpers --- --- a/pallets/nft/Cargo.toml +++ b/pallets/nft/Cargo.toml @@ -39,7 +39,7 @@ 'primitive-types/std', 'evm-coder/std', - 'evm-coder-substrate/std', + 'pallet-evm-coder-substrate/std', ] ################################################################################ @@ -144,10 +144,12 @@ sp-api = { default-features = false, version = '3.0.0', git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.8" } evm-coder = { default-features = false, path = "../../crates/evm-coder" } -evm-coder-substrate = { default-features = false, path = "../../crates/evm-coder-substrate" } -primitive-types = { version = "0.9.0", default-features = false, features = ["serde_no_std"] } +pallet-evm-coder-substrate = { default-features = false, path = "../../pallets/evm-coder-substrate" } +primitive-types = { version = "0.9.0", default-features = false, features = [ + "serde_no_std", +] } pallet-evm = { default-features = false, version = "5.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" } pallet-ethereum = { default-features = false, version = "3.0.0-dev", git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" } fp-evm = { default-features = false, version = '2.0.0', git = "https://github.com/uniquenetwork/frontier.git", branch = "injected-transactions-parachain" } -hex-literal = "0.3.1" \ No newline at end of file +hex-literal = "0.3.1" --- a/pallets/nft/src/eth/erc.rs +++ b/pallets/nft/src/eth/erc.rs @@ -1,46 +1,63 @@ -use evm_coder::{solidity_interface, solidity, types::*, ToLog}; +use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; +use evm_coder::{ToLog, execution::Result, solidity, solidity_interface, types::*}; +use core::convert::TryInto; +use alloc::format; +use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList}; +use frame_support::storage::StorageDoubleMap; +use pallet_evm::AddressMapping; +use super::account::CrossAccountId; use sp_std::vec::Vec; -#[solidity_interface] -pub trait InlineNameSymbol { - type Error; - - fn name(&self) -> Result; - fn symbol(&self) -> Result; +#[solidity_interface(name = "ERC165")] +impl CollectionHandle { + fn supports_interface(&self, interface_id: bytes4) -> Result { + Ok(match self.mode { + CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id), + CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id), + _ => false, + }) + } } -#[solidity_interface] -pub trait InlineTotalSupply { - type Error; +#[solidity_interface(name = "InlineNameSymbol")] +impl CollectionHandle { + fn name(&self) -> Result { + Ok(decode_utf16(self.name.iter().copied()) + .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) + .collect::()) + } - fn total_supply(&self) -> Result; + fn symbol(&self) -> Result { + Ok(string::from_utf8_lossy(&self.token_prefix).into()) + } } -#[solidity_interface] -pub trait ERC165 { - type Error; - - fn supports_interface(&self, interface_id: bytes4) -> Result; +#[solidity_interface(name = "InlineTotalSupply")] +impl CollectionHandle { + fn total_supply(&self) -> Result { + // TODO: we do not track total amount of all tokens + Ok(0.into()) + } } -#[solidity_interface(inline_is(InlineNameSymbol))] -pub trait ERC721Metadata { - type Error; - - #[solidity(rename_selector = "tokenURI")] - fn token_uri(&self, token_id: uint256) -> Result; +#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))] +impl CollectionHandle { + fn token_uri(&self, token_id: uint256) -> Result { + // TODO: We should standartize url prefix, maybe via offchain schema? + Ok(format!("unique.network/{}/{}", self.id, token_id)) + } } -#[solidity_interface(inline_is(InlineTotalSupply))] -pub trait ERC721Enumerable { - type Error; +#[solidity_interface(name = "ERC721Enumerable", inline_is(InlineTotalSupply))] +impl CollectionHandle { + fn token_by_index(&self, index: uint256) -> Result { + Ok(index) + } - fn token_by_index(&self, index: uint256) -> Result; - fn token_of_owner_by_index( - &self, - owner: address, - index: uint256, - ) -> Result; + fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result { + // TODO: Not implemetable + Err("not implemented".into()) + } } #[derive(ToLog)] @@ -71,29 +88,40 @@ }, } -#[solidity_interface(is(ERC165), events(ERC721Events))] -pub trait ERC721 { - type Error; - - fn balance_of(&self, owner: address) -> Result; - fn owner_of(&self, token_id: uint256) -> Result; - - #[solidity(rename_selector = "safeTransferFrom")] +#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))] +impl CollectionHandle { + #[solidity(rename_selector = "balanceOf")] + fn balance_of_nft(&self, owner: address) -> Result { + let owner = T::EvmAddressMapping::into_account_id(owner); + let balance = >::get(self.id, owner); + Ok(balance.into()) + } + fn owner_of(&self, token_id: uint256) -> Result
{ + let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; + let token = >::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; + _from: address, + _to: address, + _token_id: uint256, + _data: bytes, + _value: value, + ) -> Result { + // TODO: Not implemetable + Err("not implemented".into()) + } fn safe_transfer_from( &mut self, - from: address, - to: address, - token_id: uint256, - value: value, - ) -> Result; + _from: address, + _to: address, + _token_id: uint256, + _value: value, + ) -> Result { + // TODO: Not implemetable + Err("not implemented".into()) + } fn transfer_from( &mut self, @@ -101,53 +129,86 @@ from: address, to: address, token_id: uint256, - value: value, - ) -> Result; + _value: value, + ) -> Result { + 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")?; + + >::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; + _value: value, + ) -> Result { + 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")?; + + >::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; + _caller: caller, + _operator: address, + _approved: bool, + ) -> Result { + // TODO: Not implemetable + Err("not implemented".into()) + } + + fn get_approved(&self, _token_id: uint256) -> Result
{ + // TODO: Not implemetable + Err("not implemented".into()) + } - fn get_approved(&self, token_id: uint256) -> Result; - fn is_approved_for_all( - &self, - owner: address, - operator: address, - ) -> Result; + fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result
{ + // TODO: Not implemetable + Err("not implemented".into()) + } } -#[solidity_interface] -pub trait ERC721UniqueExtensions { - type Error; - - fn transfer( +#[solidity_interface(name = "ERC721UniqueExtensions")] +impl CollectionHandle { + #[solidity(rename_selector = "transfer")] + fn transfer_nft( &mut self, caller: caller, to: address, token_id: uint256, - value: value, - ) -> Result; + _value: value, + ) -> Result { + 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")?; + + >::transfer_internal(&caller, &to, self, token_id, 1) + .map_err(|_| "transfer error")?; + Ok(()) + } } -#[solidity_interface(is( - ERC165, - ERC721, - ERC721Metadata, - ERC721Enumerable, - ERC721UniqueExtensions -))] -pub trait UniqueNFT { - type Error; -} +#[solidity_interface( + name = "UniqueNFT", + is( + ERC165, + ERC721, + ERC721Metadata, + ERC721Enumerable, + ERC721UniqueExtensions + ) +)] +impl CollectionHandle {} #[derive(ToLog)] pub enum ERC20Events { @@ -167,35 +228,72 @@ }, } -#[solidity_interface(inline_is(InlineNameSymbol, InlineTotalSupply), events(ERC20Events))] -pub trait ERC20 { - type Error; +#[solidity_interface( + name = "ERC20", + inline_is(InlineNameSymbol, InlineTotalSupply), + events(ERC20Events) +)] +impl CollectionHandle { + fn decimals(&self) -> Result { + Ok(if let CollectionMode::Fungible(decimals) = &self.mode { + *decimals + } else { + unreachable!() + }) + } + fn balance_of(&self, owner: address) -> Result { + let owner = T::EvmAddressMapping::into_account_id(owner); + let balance = >::get(self.id, owner); + Ok(balance.into()) + } + fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let to = T::CrossAccountId::from_eth(to); + let amount = amount.try_into().map_err(|_| "amount overflow")?; - fn decimals(&self) -> Result; - fn balance_of(&self, owner: address) -> Result; - fn transfer( - &mut self, - caller: caller, - to: address, - value: uint256, - ) -> Result; - fn transfer_from( + >::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, - value: uint256, - ) -> Result; - fn approve( + amount: uint256, + ) -> Result { + 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")?; + + >::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, - value: uint256, - ) -> Result; - fn allowance(&self, owner: address, spender: address) -> Result; -} + amount: uint256, + ) -> Result { + let caller = T::CrossAccountId::from_eth(caller); + let spender = T::CrossAccountId::from_eth(spender); + let amount = amount.try_into().map_err(|_| "amount overflow")?; + + >::approve_internal(&caller, &spender, self, 1, amount) + .map_err(|_| "approve internal")?; + Ok(true) + } + fn allowance(&self, owner: address, spender: address) -> Result { + let owner = T::CrossAccountId::from_eth(owner); + let spender = T::CrossAccountId::from_eth(spender); -#[solidity_interface(is(ERC165, ERC20))] -pub trait UniqueFungible { - type Error; + Ok(>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into()) + } } + +#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))] +impl CollectionHandle {} --- a/pallets/nft/src/eth/erc_impl.rs +++ /dev/null @@ -1,283 +0,0 @@ -use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; -use evm_coder::{ - abi::{AbiWriter, StringError}, - types::*, -}; -use core::convert::TryInto; -use alloc::format; -use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList}; -use frame_support::storage::StorageDoubleMap; -use pallet_evm::AddressMapping; -use super::erc::*; -use super::account::CrossAccountId; - -type Result = core::result::Result; - -impl InlineNameSymbol for CollectionHandle { - type Error = StringError; - - fn name(&self) -> Result { - Ok(decode_utf16(self.name.iter().copied()) - .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER)) - .collect::()) - } - - fn symbol(&self) -> Result { - Ok(string::from_utf8_lossy(&self.token_prefix).into()) - } -} - -impl InlineTotalSupply for CollectionHandle { - type Error = StringError; - - fn total_supply(&self) -> Result { - // TODO: we do not track total amount of all tokens - Ok(0.into()) - } -} - -impl ERC721Metadata for CollectionHandle { - type Error = StringError; - - fn token_uri(&self, token_id: uint256) -> Result { - // TODO: We should standartize url prefix, maybe via offchain schema? - Ok(format!("unique.network/{}/{}", self.id, token_id)) - } - - fn call_inline_name_symbol(&mut self, c: Msg) -> Result { - ::call(self, c) - } -} - -impl ERC721Enumerable for CollectionHandle { - type Error = StringError; - - fn token_by_index(&self, index: uint256) -> Result { - Ok(index) - } - - fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result { - // TODO: Not implemetable - Err("not implemented".into()) - } - - fn call_inline_total_supply(&mut self, c: Msg) -> Result { - ::call(self, c) - } -} - -impl ERC721 for CollectionHandle { - type Error = StringError; - - fn balance_of(&self, owner: address) -> Result { - let owner = T::EvmAddressMapping::into_account_id(owner); - let balance = >::get(self.id, owner); - Ok(balance.into()) - } - fn owner_of(&self, token_id: uint256) -> Result
{ - let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?; - let token = >::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 { - // TODO: Not implemetable - Err("not implemented".into()) - } - fn safe_transfer_from( - &mut self, - _from: address, - _to: address, - _token_id: uint256, - _value: value, - ) -> Result { - // TODO: Not implemetable - Err("not implemented".into()) - } - - fn transfer_from( - &mut self, - caller: caller, - from: address, - to: address, - token_id: uint256, - _value: value, - ) -> Result { - 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")?; - - >::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 { - 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")?; - - >::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 { - // TODO: Not implemetable - Err("not implemented".into()) - } - - fn get_approved(&self, _token_id: uint256) -> Result
{ - // TODO: Not implemetable - Err("not implemented".into()) - } - - fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result
{ - // TODO: Not implemetable - Err("not implemented".into()) - } - - fn call_erc165(&mut self, c: Msg) -> Result { - let ERC165Call::SupportsInterface { interface_id } = c.call; - Ok(evm_coder::abi_encode!(bool( - &ERC721Call::supports_interface(interface_id) - ))) - } -} - -impl ERC721UniqueExtensions for CollectionHandle { - type Error = StringError; - fn transfer( - &mut self, - caller: caller, - to: address, - token_id: uint256, - _value: value, - ) -> Result { - 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")?; - - >::transfer_internal(&caller, &to, self, token_id, 1) - .map_err(|_| "transfer error")?; - Ok(()) - } -} - -impl UniqueNFT for CollectionHandle { - type Error = StringError; - fn call_erc165(&mut self, c: Msg) -> Result { - let ERC165Call::SupportsInterface { interface_id } = c.call; - Ok(evm_coder::abi_encode!(bool( - &UniqueNFTCall::supports_interface(interface_id) - ))) - } - fn call_erc721(&mut self, c: Msg) -> Result { - ::call(self, c) - } - fn call_erc721_metadata(&mut self, c: Msg) -> Result { - ::call(self, c) - } - fn call_erc721_enumerable(&mut self, c: Msg) -> Result { - ::call(self, c) - } - fn call_erc721_unique_extensions( - &mut self, - c: Msg, - ) -> Result { - ::call(self, c) - } -} - -impl ERC20 for CollectionHandle { - type Error = StringError; - fn decimals(&self) -> Result { - Ok(if let CollectionMode::Fungible(decimals) = &self.mode { - *decimals - } else { - unreachable!() - }) - } - fn balance_of(&self, owner: address) -> Result { - let owner = T::EvmAddressMapping::into_account_id(owner); - let balance = >::get(self.id, owner); - Ok(balance.into()) - } - fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result { - let caller = T::CrossAccountId::from_eth(caller); - let to = T::CrossAccountId::from_eth(to); - let amount = amount.try_into().map_err(|_| "amount overflow")?; - - >::transfer_internal(&caller, &to, self, 1, amount) - .map_err(|_| "transfer error")?; - Ok(true) - } - fn transfer_from( - &mut self, - caller: caller, - from: address, - to: address, - amount: uint256, - ) -> Result { - 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")?; - - >::transfer_from_internal(&caller, &from, &to, self, 1, amount) - .map_err(|_| "transferFrom error")?; - Ok(true) - } - fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result { - let caller = T::CrossAccountId::from_eth(caller); - let spender = T::CrossAccountId::from_eth(spender); - let amount = amount.try_into().map_err(|_| "amount overflow")?; - - >::approve_internal(&caller, &spender, self, 1, amount) - .map_err(|_| "approve internal")?; - Ok(true) - } - fn allowance(&self, owner: address, spender: address) -> Result { - let owner = T::CrossAccountId::from_eth(owner); - let spender = T::CrossAccountId::from_eth(spender); - - Ok(>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into()) - } - fn call_inline_name_symbol(&mut self, c: Msg) -> Result { - ::call(self, c) - } - fn call_inline_total_supply(&mut self, c: Msg) -> Result { - ::call(self, c) - } -} - -impl UniqueFungible for CollectionHandle { - type Error = StringError; - fn call_erc165(&mut self, c: Msg) -> Result { - let ERC165Call::SupportsInterface { interface_id } = c.call; - Ok(evm_coder::abi_encode!(bool( - &UniqueNFTCall::supports_interface(interface_id) - ))) - } - fn call_erc20(&mut self, c: Msg) -> Result { - ::call(self, c) - } -} --- a/pallets/nft/src/eth/log.rs +++ /dev/null @@ -1,19 +0,0 @@ -use sp_std::cell::RefCell; -use sp_std::vec::Vec; - -use ethereum::Log; - -#[derive(Default)] -pub struct LogRecorder(RefCell>); - -impl LogRecorder { - pub fn is_empty(&self) -> bool { - self.0.borrow().is_empty() - } - pub fn log(&self, log: Log) { - self.0.borrow_mut().push(log); - } - pub fn retrieve_logs(self) -> Vec { - self.0.into_inner() - } -} --- a/pallets/nft/src/eth/mod.rs +++ b/pallets/nft/src/eth/mod.rs @@ -1,23 +1,15 @@ pub mod account; pub mod erc; -mod erc_impl; -pub mod log; pub mod sponsoring; -use evm_coder::abi::AbiWriter; -use evm_coder::abi::StringError; -use sp_std::prelude::*; +use pallet_evm_coder_substrate::call_internal; use sp_std::borrow::ToOwned; use sp_std::vec::Vec; - -use pallet_evm::{PrecompileOutput, ExitReason, ExitRevert, ExitSucceed}; +use pallet_evm::{PrecompileOutput}; use sp_core::{H160, U256}; use frame_support::storage::StorageMap; - use crate::{Config, CollectionById, CollectionHandle, CollectionId, CollectionMode}; - -use erc::{UniqueFungible, UniqueFungibleCall, UniqueNFT, UniqueNFTCall}; -use evm_coder::{types::*, abi::AbiReader}; +use erc::{UniqueFungibleCall, UniqueNFTCall}; pub struct NftErcSupport(core::marker::PhantomData); @@ -42,19 +34,17 @@ H160(out) } +/* fn call_internal( collection: &mut CollectionHandle, caller: caller, method_id: u32, mut input: AbiReader, value: U256, -) -> Result, evm_coder::abi::StringError> { +) -> Result> { match collection.mode.clone() { CollectionMode::Fungible(_) => { - #[cfg(feature = "std")] - { - println!("Parse fungible call {:x}", method_id); - } + call_internal(); let call = match UniqueFungibleCall::parse(method_id, &mut input)? { Some(v) => v, None => { @@ -65,10 +55,6 @@ return Ok(None); } }; - #[cfg(feature = "std")] - { - dbg!(&call); - } Ok(Some( as UniqueFungible>::call( collection, Msg { @@ -92,11 +78,9 @@ }, )?)) } - _ => Err(StringError::from( - "erc calls only supported to fungible and nft collections for now", - )), + _ => Err("erc calls only supported to fungible and nft collections for now".into()), } -} +}*/ impl pallet_evm::OnMethodCall for NftErcSupport { fn is_reserved(target: &H160) -> bool { @@ -129,56 +113,15 @@ ) -> Option { let mut collection = map_eth_to_id(target) .and_then(|id| >::get_with_gas_limit(id, gas_limit))?; - let (method_id, input) = AbiReader::new_call(input).unwrap(); - let result = call_internal(&mut collection, *source, method_id, input, value); - let cost = gas_limit - collection.gas_left(); - let logs = collection.logs.retrieve_logs(); - match result { - Ok(Some(v)) => Some(PrecompileOutput { - exit_status: ExitReason::Succeed(ExitSucceed::Returned), - cost, - logs, - output: v.finish(), - }), - Ok(None) => None, - Err(e) => Some(PrecompileOutput { - exit_status: ExitReason::Revert(ExitRevert::Reverted), - cost: 0, - logs: Default::default(), - output: AbiWriter::from(e).finish(), - }), - } - } -} - -// TODO: This function is slow, and output can be memoized -pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction { - // FIXME: Can be done on wasm runtime with https://github.com/paritytech/substrate/pull/8728 - #[cfg(feature = "std")] - { - let contract = collection_id_to_address(collection_id); - let signed = ethereum_tx_sign::RawTransaction { - nonce: 0.into(), - to: Some(contract.0.into()), - value: 0.into(), - gas_price: 0.into(), - gas: 0.into(), - // zero selector, this transaction always have same sender, so all data should be acquired from logs - data: Vec::from([0, 0, 0, 0]), - } - .sign( - // TODO: move to pallet config - // 0xF70631E55faff9f3FD3681545aa6c724226a3853 - // 9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a - &hex_literal::hex!("9dbaef9b3ebc00e53f67c6a77bcfbf2c4f2aebe4d70d94af4f2df01744b7a91a") - .into(), - &chain_id, - ); - rlp::decode::(&signed) - .expect("transaction is just created, it can't be broken") - } - #[cfg(not(feature = "std"))] - { - panic!("transaction generation not yet supported by wasm runtime while generating transaction for collection_id {}, chain_id {}", collection_id, chain_id) + let result = match collection.mode { + CollectionMode::NFT => { + call_internal::(*source, &mut collection, value, input) + } + CollectionMode::Fungible(_) => { + call_internal::(*source, &mut collection, value, input) + } + _ => return None, + }; + collection.recorder.evm_to_precompile_output(result) } } --- a/pallets/nft/src/lib.rs +++ b/pallets/nft/src/lib.rs @@ -36,14 +36,12 @@ use sp_std::vec; use sp_runtime::sp_std::prelude::Vec; use core::ops::{Deref, DerefMut}; -use core::cell::RefCell; use nft_data_structs::{ MAX_DECIMAL_POINTS, MAX_SPONSOR_TIMEOUT, MAX_TOKEN_OWNERSHIP, MAX_REFUNGIBLE_PIECES, AccessMode, ChainLimits, Collection, CreateItemData, CollectionLimits, CollectionId, CollectionMode, TokenId, SchemaVersion, SponsorshipState, Ownership, NftItemType, FungibleItemType, ReFungibleItemType, }; -use pallet_ethereum::EthereumTransactionSender; #[cfg(test)] mod mock; @@ -55,6 +53,7 @@ mod eth; mod sponsorship; pub use sponsorship::NftSponsorshipHandler; +pub use eth::sponsoring::NftEthSponsorshipHandler; pub use eth::NftErcSupport; pub use eth::account::*; @@ -178,42 +177,39 @@ } } +#[must_use = "Should call submit_logs or save, otherwise some data will be lost for evm side"] pub struct CollectionHandle { pub id: CollectionId, collection: Collection, - logs: eth::log::LogRecorder, - evm_address: H160, - gas_limit: RefCell, + recorder: pallet_evm_coder_substrate::SubstrateRecorder, } impl CollectionHandle { pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option { >::get(id).map(|collection| Self { id, collection, - logs: eth::log::LogRecorder::default(), - evm_address: eth::collection_id_to_address(id), - gas_limit: RefCell::new(gas_limit), + recorder: pallet_evm_coder_substrate::SubstrateRecorder::new( + eth::collection_id_to_address(id), + gas_limit, + ), }) } pub fn get(id: CollectionId) -> Option { Self::get_with_gas_limit(id, u64::MAX) } - pub fn gas_left(&self) -> u64 { - *self.gas_limit.borrow() + pub fn log(&self, log: impl evm_coder::ToLog) -> DispatchResult { + self.recorder.log_sub(log) } - pub fn consume_gas(&self, gas: u64) -> DispatchResult { - let mut gas_limit = self.gas_limit.borrow_mut(); - if *gas_limit < gas { - fail!(Error::::OutOfGas); - } - *gas_limit -= gas; - Ok(()) + fn consume_gas(&self, gas: u64) -> DispatchResult { + self.recorder.consume_gas_sub(gas) } - pub fn log(&self, log: impl evm_coder::ToLog) { - self.logs.log(log.to_log(self.evm_address)) + pub fn submit_logs(self) -> DispatchResult { + self.recorder.submit_logs() } - pub fn into_inner(self) -> Collection { - self.collection + pub fn save(self) -> DispatchResult { + self.recorder.submit_logs()?; + >::insert(self.id, self.collection); + Ok(()) } } impl Deref for CollectionHandle { @@ -230,7 +226,7 @@ } } -pub trait Config: system::Config + Sized { +pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized { type Event: From> + Into<::Event>; /// Weight information for extrinsics in this pallet. @@ -245,9 +241,6 @@ <::Currency as Currency>::Balance, >; type TreasuryAccountId: Get; - - type EthereumChainId: Get; - type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender; } // # Used definitions @@ -564,23 +557,23 @@ fail!(Error::::NoPermission); } - >::remove_prefix(collection_id); - >::remove_prefix(collection_id); - >::remove_prefix(collection_id); + >::remove_prefix(collection_id, None); + >::remove_prefix(collection_id, None); + >::remove_prefix(collection_id, None); ::remove(collection_id); >::remove(collection_id); >::remove(collection_id); - >::remove_prefix(collection_id); + >::remove_prefix(collection_id, None); - >::remove_prefix(collection_id); - >::remove_prefix(collection_id); - >::remove_prefix(collection_id); + >::remove_prefix(collection_id, None); + >::remove_prefix(collection_id, None); + >::remove_prefix(collection_id, None); - >::remove_prefix(collection_id); - >::remove_prefix(collection_id); - >::remove_prefix(collection_id); + >::remove_prefix(collection_id, None); + >::remove_prefix(collection_id, None); + >::remove_prefix(collection_id, None); - >::remove_prefix(collection_id); + >::remove_prefix(collection_id, None); DestroyedCollectionCount::put(DestroyedCollectionCount::get() .checked_add(1) @@ -667,9 +660,7 @@ let mut target_collection = Self::get_collection(collection_id)?; Self::check_owner_permissions(&target_collection, &sender)?; target_collection.access = mode; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Allows Anyone to create tokens if: @@ -694,9 +685,7 @@ let mut target_collection = Self::get_collection(collection_id)?; Self::check_owner_permissions(&target_collection, &sender)?; target_collection.mint_mode = mint_permission; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Change the owner of the collection. @@ -718,9 +707,7 @@ let mut target_collection = Self::get_collection(collection_id)?; Self::check_owner_permissions(&target_collection, &sender)?; target_collection.owner = new_owner; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Adds an admin of the Collection. @@ -800,9 +787,7 @@ Self::check_owner_permissions(&target_collection, &sender)?; target_collection.sponsorship = SponsorshipState::Unconfirmed(new_sponsor); - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// # Permissions @@ -824,9 +809,7 @@ ); target_collection.sponsorship = SponsorshipState::Confirmed(sender); - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Switch back to pay-per-own-transaction model. @@ -847,9 +830,7 @@ Self::check_owner_permissions(&target_collection, &sender)?; target_collection.sponsorship = SponsorshipState::Disabled; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// This method creates a concrete instance of NFT Collection created with CreateCollection method. @@ -884,8 +865,7 @@ Self::create_item_internal(&sender, &collection, &owner, data)?; - Self::submit_logs(collection)?; - Ok(()) + collection.submit_logs() } /// This method creates multiple items in a collection created with CreateCollection method. @@ -918,8 +898,7 @@ Self::create_multiple_items_internal(&sender, &collection, &owner, items_data)?; - Self::submit_logs(collection)?; - Ok(()) + collection.submit_logs() } /// Destroys a concrete instance of NFT. @@ -944,8 +923,7 @@ Self::burn_item_internal(&sender, &target_collection, item_id, value)?; - Self::submit_logs(target_collection)?; - Ok(()) + target_collection.submit_logs() } /// Change ownership of the token. @@ -979,8 +957,7 @@ Self::transfer_internal(&sender, &recipient, &collection, item_id, value)?; - Self::submit_logs(collection)?; - Ok(()) + collection.submit_logs() } /// Set, change, or remove approved address to transfer the ownership of the NFT. @@ -1006,8 +983,7 @@ Self::approve_internal(&sender, &spender, &collection, item_id, amount)?; - Self::submit_logs(collection)?; - Ok(()) + collection.submit_logs() } /// 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. @@ -1037,8 +1013,7 @@ Self::transfer_from_internal(&sender, &from, &recipient, &collection, item_id, value)?; - Self::submit_logs(collection)?; - Ok(()) + collection.submit_logs() } // #[weight = 0] // // let no_perm_mes = "You do not have permissions to modify this collection"; @@ -1107,9 +1082,7 @@ let mut target_collection = Self::get_collection(collection_id)?; Self::check_owner_or_admin_permissions(&target_collection, &sender)?; target_collection.schema_version = version; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Set off-chain data schema. @@ -1139,9 +1112,7 @@ ensure!(schema.len() as u32 <= ChainLimit::get().offchain_schema_limit, ""); target_collection.offchain_schema = schema; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Set const on-chain data schema. @@ -1171,9 +1142,7 @@ ensure!(schema.len() as u32 <= ChainLimit::get().const_on_chain_schema_limit, ""); target_collection.const_on_chain_schema = schema; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } /// Set variable on-chain data schema. @@ -1203,9 +1172,7 @@ ensure!(schema.len() as u32 <= ChainLimit::get().variable_on_chain_schema_limit, ""); target_collection.variable_on_chain_schema = schema; - Self::save_collection(target_collection); - - Ok(()) + target_collection.save() } // Sudo permissions function @@ -1253,9 +1220,8 @@ ); target_collection.limits = new_limits; - Self::save_collection(target_collection); - Ok(()) + target_collection.save() } } } @@ -1376,7 +1342,7 @@ owner: *sender.as_eth(), approved: *spender.as_eth(), token_id: item_id.into(), - }); + })?; } if matches!(collection.mode, CollectionMode::Fungible(_)) { @@ -1385,7 +1351,7 @@ owner: *sender.as_eth(), spender: *spender.as_eth(), value: allowance.into(), - }); + })?; } Self::deposit_event(RawEvent::Approved( @@ -1461,7 +1427,7 @@ owner: *from.as_eth(), spender: *sender.as_eth(), value: allowance.into(), - }); + })?; } Ok(()) @@ -1789,7 +1755,7 @@ from: H160::default(), to: *item_owner.as_eth(), token_id: current_index.into(), - }); + })?; Self::deposit_event(RawEvent::ItemCreated( collection_id, current_index, @@ -1886,7 +1852,7 @@ from: *owner.as_eth(), to: H160::default(), value: value.into(), - }); + })?; Ok(()) } @@ -1894,22 +1860,8 @@ collection_id: CollectionId, ) -> Result, sp_runtime::DispatchError> { Ok(>::get(collection_id).ok_or(Error::::CollectionNotFound)?) - } - - fn save_collection(collection: CollectionHandle) { - >::insert(collection.id, collection.into_inner()); } - pub fn submit_logs(collection: CollectionHandle) -> DispatchResult { - if collection.logs.is_empty() { - return Ok(()); - } - T::EthereumTransactionSender::submit_logs_transaction( - eth::generate_transaction(collection.id, T::EthereumChainId::get()), - collection.logs.retrieve_logs(), - ) - } - fn check_owner_permissions( target_collection: &CollectionHandle, subject: &T::AccountId, @@ -2037,7 +1989,7 @@ from: *owner.as_eth(), to: *recipient.as_eth(), value: value.into(), - }); + })?; Self::deposit_event(RawEvent::Transfer( collection.id, 1, @@ -2173,7 +2125,7 @@ from: *sender.as_eth(), to: *new_owner.as_eth(), token_id: item_id.into(), - }); + })?; Self::deposit_event(RawEvent::Transfer( collection.id, item_id, --- a/pallets/nft/src/mock.rs +++ b/pallets/nft/src/mock.rs @@ -193,7 +193,6 @@ type EvmAddressMapping = TestEvmAddressMapping; type EvmBackwardsAddressMapping = TestEvmBackwardsAddressMapping; type CrossAccountId = TestCrossAccountId; - type EthereumChainId = EthereumChainId; type EthereumTransactionSender = TestEtheremTransactionSender; } -- gitstuff