difftreelog
refactor rewrite nft pallet to evm helpers
in: master
7 files changed
pallets/nft/Cargo.tomldiffbeforeafterboth--- 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"
pallets/nft/src/eth/erc.rsdiffbeforeafterboth--- 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<string, Self::Error>;
- fn symbol(&self) -> Result<string, Self::Error>;
+#[solidity_interface(name = "ERC165")]
+impl<T: Config> CollectionHandle<T> {
+ fn supports_interface(&self, interface_id: bytes4) -> Result<bool> {
+ Ok(match self.mode {
+ CollectionMode::Fungible(_) => UniqueFungibleCall::supports_interface(interface_id),
+ CollectionMode::NFT => UniqueNFTCall::supports_interface(interface_id),
+ _ => false,
+ })
+ }
}
-#[solidity_interface]
-pub trait InlineTotalSupply {
- type Error;
+#[solidity_interface(name = "InlineNameSymbol")]
+impl<T: Config> CollectionHandle<T> {
+ fn name(&self) -> Result<string> {
+ Ok(decode_utf16(self.name.iter().copied())
+ .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
+ .collect::<string>())
+ }
- fn total_supply(&self) -> Result<uint256, Self::Error>;
+ fn symbol(&self) -> Result<string> {
+ Ok(string::from_utf8_lossy(&self.token_prefix).into())
+ }
}
-#[solidity_interface]
-pub trait ERC165 {
- type Error;
-
- fn supports_interface(&self, interface_id: bytes4) -> Result<bool, Self::Error>;
+#[solidity_interface(name = "InlineTotalSupply")]
+impl<T: Config> CollectionHandle<T> {
+ fn total_supply(&self) -> Result<uint256> {
+ // TODO: we do not track total amount of all tokens
+ Ok(0.into())
+ }
}
-#[solidity_interface(inline_is(InlineNameSymbol))]
-pub trait ERC721Metadata {
- type Error;
-
- #[solidity(rename_selector = "tokenURI")]
- fn token_uri(&self, token_id: uint256) -> Result<string, Self::Error>;
+#[solidity_interface(name = "ERC721Metadata", inline_is(InlineNameSymbol))]
+impl<T: Config> CollectionHandle<T> {
+ fn token_uri(&self, token_id: uint256) -> Result<string> {
+ // 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<T: Config> CollectionHandle<T> {
+ fn token_by_index(&self, index: uint256) -> Result<uint256> {
+ Ok(index)
+ }
- fn token_by_index(&self, index: uint256) -> Result<uint256, Self::Error>;
- fn token_of_owner_by_index(
- &self,
- owner: address,
- index: uint256,
- ) -> Result<uint256, Self::Error>;
+ fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {
+ // 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<uint256, Self::Error>;
- fn owner_of(&self, token_id: uint256) -> Result<address, Self::Error>;
-
- #[solidity(rename_selector = "safeTransferFrom")]
+#[solidity_interface(name = "ERC721", is(ERC165), events(ERC721Events))]
+impl<T: Config> CollectionHandle<T> {
+ #[solidity(rename_selector = "balanceOf")]
+ fn balance_of_nft(&self, owner: address) -> Result<uint256> {
+ let owner = T::EvmAddressMapping::into_account_id(owner);
+ let balance = <Balance<T>>::get(self.id, owner);
+ Ok(balance.into())
+ }
+ fn owner_of(&self, token_id: uint256) -> Result<address> {
+ let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;
+ let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;
+ Ok(*token.owner.as_eth())
+ }
fn safe_transfer_from_with_data(
&mut self,
- from: address,
- to: address,
- token_id: uint256,
- data: bytes,
- value: value,
- ) -> Result<void, Self::Error>;
+ _from: address,
+ _to: address,
+ _token_id: uint256,
+ _data: bytes,
+ _value: value,
+ ) -> Result<void> {
+ // TODO: Not implemetable
+ Err("not implemented".into())
+ }
fn safe_transfer_from(
&mut self,
- from: address,
- to: address,
- token_id: uint256,
- value: value,
- ) -> Result<void, Self::Error>;
+ _from: address,
+ _to: address,
+ _token_id: uint256,
+ _value: value,
+ ) -> Result<void> {
+ // 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<void, Self::Error>;
+ _value: value,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = T::CrossAccountId::from_eth(from);
+ let to = T::CrossAccountId::from_eth(to);
+ let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
+
+ <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)
+ .map_err(|_| "transferFrom error")?;
+ Ok(())
+ }
+
fn approve(
&mut self,
caller: caller,
approved: address,
token_id: uint256,
- value: value,
- ) -> Result<void, Self::Error>;
+ _value: value,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let approved = T::CrossAccountId::from_eth(approved);
+ let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;
+
+ <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)
+ .map_err(|_| "approve internal")?;
+ Ok(())
+ }
+
fn set_approval_for_all(
&mut self,
- caller: caller,
- operator: address,
- approved: bool,
- ) -> Result<void, Self::Error>;
+ _caller: caller,
+ _operator: address,
+ _approved: bool,
+ ) -> Result<void> {
+ // TODO: Not implemetable
+ Err("not implemented".into())
+ }
+
+ fn get_approved(&self, _token_id: uint256) -> Result<address> {
+ // TODO: Not implemetable
+ Err("not implemented".into())
+ }
- fn get_approved(&self, token_id: uint256) -> Result<address, Self::Error>;
- fn is_approved_for_all(
- &self,
- owner: address,
- operator: address,
- ) -> Result<address, Self::Error>;
+ fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {
+ // TODO: Not implemetable
+ Err("not implemented".into())
+ }
}
-#[solidity_interface]
-pub trait ERC721UniqueExtensions {
- type Error;
-
- fn transfer(
+#[solidity_interface(name = "ERC721UniqueExtensions")]
+impl<T: Config> CollectionHandle<T> {
+ #[solidity(rename_selector = "transfer")]
+ fn transfer_nft(
&mut self,
caller: caller,
to: address,
token_id: uint256,
- value: value,
- ) -> Result<void, Self::Error>;
+ _value: value,
+ ) -> Result<void> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let to = T::CrossAccountId::from_eth(to);
+ let token_id = token_id.try_into().map_err(|_| "amount overflow")?;
+
+ <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)
+ .map_err(|_| "transfer error")?;
+ Ok(())
+ }
}
-#[solidity_interface(is(
- ERC165,
- ERC721,
- ERC721Metadata,
- ERC721Enumerable,
- ERC721UniqueExtensions
-))]
-pub trait UniqueNFT {
- type Error;
-}
+#[solidity_interface(
+ name = "UniqueNFT",
+ is(
+ ERC165,
+ ERC721,
+ ERC721Metadata,
+ ERC721Enumerable,
+ ERC721UniqueExtensions
+ )
+)]
+impl<T: Config> CollectionHandle<T> {}
#[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<T: Config> CollectionHandle<T> {
+ fn decimals(&self) -> Result<uint8> {
+ Ok(if let CollectionMode::Fungible(decimals) = &self.mode {
+ *decimals
+ } else {
+ unreachable!()
+ })
+ }
+ fn balance_of(&self, owner: address) -> Result<uint256> {
+ let owner = T::EvmAddressMapping::into_account_id(owner);
+ let balance = <Balance<T>>::get(self.id, owner);
+ Ok(balance.into())
+ }
+ fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let to = T::CrossAccountId::from_eth(to);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
- fn decimals(&self) -> Result<uint8, Self::Error>;
- fn balance_of(&self, owner: address) -> Result<uint256, Self::Error>;
- fn transfer(
- &mut self,
- caller: caller,
- to: address,
- value: uint256,
- ) -> Result<bool, Self::Error>;
- fn transfer_from(
+ <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)
+ .map_err(|_| "transfer error")?;
+ Ok(true)
+ }
+ #[solidity(rename_selector = "transferFrom")]
+ fn transfer_from_fungible(
&mut self,
caller: caller,
from: address,
to: address,
- value: uint256,
- ) -> Result<bool, Self::Error>;
- fn approve(
+ amount: uint256,
+ ) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let from = T::CrossAccountId::from_eth(from);
+ let to = T::CrossAccountId::from_eth(to);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+
+ <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)
+ .map_err(|_| "transferFrom error")?;
+ Ok(true)
+ }
+ #[solidity(rename_selector = "approve")]
+ fn approve_fungible(
&mut self,
caller: caller,
spender: address,
- value: uint256,
- ) -> Result<bool, Self::Error>;
- fn allowance(&self, owner: address, spender: address) -> Result<uint256, Self::Error>;
-}
+ amount: uint256,
+ ) -> Result<bool> {
+ let caller = T::CrossAccountId::from_eth(caller);
+ let spender = T::CrossAccountId::from_eth(spender);
+ let amount = amount.try_into().map_err(|_| "amount overflow")?;
+
+ <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)
+ .map_err(|_| "approve internal")?;
+ Ok(true)
+ }
+ fn allowance(&self, owner: address, spender: address) -> Result<uint256> {
+ let owner = T::CrossAccountId::from_eth(owner);
+ let spender = T::CrossAccountId::from_eth(spender);
-#[solidity_interface(is(ERC165, ERC20))]
-pub trait UniqueFungible {
- type Error;
+ Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())
+ }
}
+
+#[solidity_interface(name = "UniqueFungible", is(ERC165, ERC20))]
+impl<T: Config> CollectionHandle<T> {}
pallets/nft/src/eth/erc_impl.rsdiffbeforeafterboth1use core::char::{decode_utf16, REPLACEMENT_CHARACTER};2use evm_coder::{3 abi::{AbiWriter, StringError},4 types::*,5};6use core::convert::TryInto;7use alloc::format;8use crate::{Allowances, Module, Balance, CollectionHandle, CollectionMode, Config, NftItemList};9use frame_support::storage::StorageDoubleMap;10use pallet_evm::AddressMapping;11use super::erc::*;12use super::account::CrossAccountId;1314type Result<T> = core::result::Result<T, StringError>;1516impl<T: Config> InlineNameSymbol for CollectionHandle<T> {17 type Error = StringError;1819 fn name(&self) -> Result<string> {20 Ok(decode_utf16(self.name.iter().copied())21 .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))22 .collect::<string>())23 }2425 fn symbol(&self) -> Result<string> {26 Ok(string::from_utf8_lossy(&self.token_prefix).into())27 }28}2930impl<T: Config> InlineTotalSupply for CollectionHandle<T> {31 type Error = StringError;3233 fn total_supply(&self) -> Result<uint256> {34 // TODO: we do not track total amount of all tokens35 Ok(0.into())36 }37}3839impl<T: Config> ERC721Metadata for CollectionHandle<T> {40 type Error = StringError;4142 fn token_uri(&self, token_id: uint256) -> Result<string> {43 // TODO: We should standartize url prefix, maybe via offchain schema?44 Ok(format!("unique.network/{}/{}", self.id, token_id))45 }4647 fn call_inline_name_symbol(&mut self, c: Msg<InlineNameSymbolCall>) -> Result<AbiWriter> {48 <Self as InlineNameSymbol>::call(self, c)49 }50}5152impl<T: Config> ERC721Enumerable for CollectionHandle<T> {53 type Error = StringError;5455 fn token_by_index(&self, index: uint256) -> Result<uint256> {56 Ok(index)57 }5859 fn token_of_owner_by_index(&self, _owner: address, _index: uint256) -> Result<uint256> {60 // TODO: Not implemetable61 Err("not implemented".into())62 }6364 fn call_inline_total_supply(&mut self, c: Msg<InlineTotalSupplyCall>) -> Result<AbiWriter> {65 <Self as InlineTotalSupply>::call(self, c)66 }67}6869impl<T: Config> ERC721 for CollectionHandle<T> {70 type Error = StringError;7172 fn balance_of(&self, owner: address) -> Result<uint256> {73 let owner = T::EvmAddressMapping::into_account_id(owner);74 let balance = <Balance<T>>::get(self.id, owner);75 Ok(balance.into())76 }77 fn owner_of(&self, token_id: uint256) -> Result<address> {78 let token_id: u32 = token_id.try_into().map_err(|_| "token id overflow")?;79 let token = <NftItemList<T>>::get(self.id, token_id).ok_or("unknown token")?;80 Ok(*token.owner.as_eth())81 }82 fn safe_transfer_from_with_data(83 &mut self,84 _from: address,85 _to: address,86 _token_id: uint256,87 _data: bytes,88 _value: value,89 ) -> Result<void> {90 // TODO: Not implemetable91 Err("not implemented".into())92 }93 fn safe_transfer_from(94 &mut self,95 _from: address,96 _to: address,97 _token_id: uint256,98 _value: value,99 ) -> Result<void> {100 // TODO: Not implemetable101 Err("not implemented".into())102 }103104 fn transfer_from(105 &mut self,106 caller: caller,107 from: address,108 to: address,109 token_id: uint256,110 _value: value,111 ) -> Result<void> {112 let caller = T::CrossAccountId::from_eth(caller);113 let from = T::CrossAccountId::from_eth(from);114 let to = T::CrossAccountId::from_eth(to);115 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;116117 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, token_id, 1)118 .map_err(|_| "transferFrom error")?;119 Ok(())120 }121122 fn approve(123 &mut self,124 caller: caller,125 approved: address,126 token_id: uint256,127 _value: value,128 ) -> Result<void> {129 let caller = T::CrossAccountId::from_eth(caller);130 let approved = T::CrossAccountId::from_eth(approved);131 let token_id = token_id.try_into().map_err(|_| "token_id overflow")?;132133 <Module<T>>::approve_internal(&caller, &approved, self, token_id, 1)134 .map_err(|_| "approve internal")?;135 Ok(())136 }137138 fn set_approval_for_all(139 &mut self,140 _caller: caller,141 _operator: address,142 _approved: bool,143 ) -> Result<void> {144 // TODO: Not implemetable145 Err("not implemented".into())146 }147148 fn get_approved(&self, _token_id: uint256) -> Result<address> {149 // TODO: Not implemetable150 Err("not implemented".into())151 }152153 fn is_approved_for_all(&self, _owner: address, _operator: address) -> Result<address> {154 // TODO: Not implemetable155 Err("not implemented".into())156 }157158 fn call_erc165(&mut self, c: Msg<ERC165Call>) -> Result<AbiWriter> {159 let ERC165Call::SupportsInterface { interface_id } = c.call;160 Ok(evm_coder::abi_encode!(bool(161 &ERC721Call::supports_interface(interface_id)162 )))163 }164}165166impl<T: Config> ERC721UniqueExtensions for CollectionHandle<T> {167 type Error = StringError;168 fn transfer(169 &mut self,170 caller: caller,171 to: address,172 token_id: uint256,173 _value: value,174 ) -> Result<void> {175 let caller = T::CrossAccountId::from_eth(caller);176 let to = T::CrossAccountId::from_eth(to);177 let token_id = token_id.try_into().map_err(|_| "amount overflow")?;178179 <Module<T>>::transfer_internal(&caller, &to, self, token_id, 1)180 .map_err(|_| "transfer error")?;181 Ok(())182 }183}184185impl<T: Config> UniqueNFT for CollectionHandle<T> {186 type Error = StringError;187 fn call_erc165(&mut self, c: Msg<ERC165Call>) -> Result<AbiWriter> {188 let ERC165Call::SupportsInterface { interface_id } = c.call;189 Ok(evm_coder::abi_encode!(bool(190 &UniqueNFTCall::supports_interface(interface_id)191 )))192 }193 fn call_erc721(&mut self, c: Msg<ERC721Call>) -> Result<AbiWriter> {194 <Self as ERC721>::call(self, c)195 }196 fn call_erc721_metadata(&mut self, c: Msg<ERC721MetadataCall>) -> Result<AbiWriter> {197 <Self as ERC721Metadata>::call(self, c)198 }199 fn call_erc721_enumerable(&mut self, c: Msg<ERC721EnumerableCall>) -> Result<AbiWriter> {200 <Self as ERC721Enumerable>::call(self, c)201 }202 fn call_erc721_unique_extensions(203 &mut self,204 c: Msg<ERC721UniqueExtensionsCall>,205 ) -> Result<AbiWriter> {206 <Self as ERC721UniqueExtensions>::call(self, c)207 }208}209210impl<T: Config> ERC20 for CollectionHandle<T> {211 type Error = StringError;212 fn decimals(&self) -> Result<uint8> {213 Ok(if let CollectionMode::Fungible(decimals) = &self.mode {214 *decimals215 } else {216 unreachable!()217 })218 }219 fn balance_of(&self, owner: address) -> Result<uint256> {220 let owner = T::EvmAddressMapping::into_account_id(owner);221 let balance = <Balance<T>>::get(self.id, owner);222 Ok(balance.into())223 }224 fn transfer(&mut self, caller: caller, to: address, amount: uint256) -> Result<bool> {225 let caller = T::CrossAccountId::from_eth(caller);226 let to = T::CrossAccountId::from_eth(to);227 let amount = amount.try_into().map_err(|_| "amount overflow")?;228229 <Module<T>>::transfer_internal(&caller, &to, self, 1, amount)230 .map_err(|_| "transfer error")?;231 Ok(true)232 }233 fn transfer_from(234 &mut self,235 caller: caller,236 from: address,237 to: address,238 amount: uint256,239 ) -> Result<bool> {240 let caller = T::CrossAccountId::from_eth(caller);241 let from = T::CrossAccountId::from_eth(from);242 let to = T::CrossAccountId::from_eth(to);243 let amount = amount.try_into().map_err(|_| "amount overflow")?;244245 <Module<T>>::transfer_from_internal(&caller, &from, &to, self, 1, amount)246 .map_err(|_| "transferFrom error")?;247 Ok(true)248 }249 fn approve(&mut self, caller: caller, spender: address, amount: uint256) -> Result<bool> {250 let caller = T::CrossAccountId::from_eth(caller);251 let spender = T::CrossAccountId::from_eth(spender);252 let amount = amount.try_into().map_err(|_| "amount overflow")?;253254 <Module<T>>::approve_internal(&caller, &spender, self, 1, amount)255 .map_err(|_| "approve internal")?;256 Ok(true)257 }258 fn allowance(&self, owner: address, spender: address) -> Result<uint256> {259 let owner = T::CrossAccountId::from_eth(owner);260 let spender = T::CrossAccountId::from_eth(spender);261262 Ok(<Allowances<T>>::get(self.id, (1, owner.as_sub(), spender.as_sub())).into())263 }264 fn call_inline_name_symbol(&mut self, c: Msg<InlineNameSymbolCall>) -> Result<AbiWriter> {265 <Self as InlineNameSymbol>::call(self, c)266 }267 fn call_inline_total_supply(&mut self, c: Msg<InlineTotalSupplyCall>) -> Result<AbiWriter> {268 <Self as InlineTotalSupply>::call(self, c)269 }270}271272impl<T: Config> UniqueFungible for CollectionHandle<T> {273 type Error = StringError;274 fn call_erc165(&mut self, c: Msg<ERC165Call>) -> Result<AbiWriter> {275 let ERC165Call::SupportsInterface { interface_id } = c.call;276 Ok(evm_coder::abi_encode!(bool(277 &UniqueNFTCall::supports_interface(interface_id)278 )))279 }280 fn call_erc20(&mut self, c: Msg<ERC20Call>) -> Result<AbiWriter> {281 <Self as ERC20>::call(self, c)282 }283}pallets/nft/src/eth/log.rsdiffbeforeafterboth--- 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<Vec<Log>>);
-
-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<Log> {
- self.0.into_inner()
- }
-}
pallets/nft/src/eth/mod.rsdiffbeforeafterboth--- 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<T: Config>(core::marker::PhantomData<T>);
@@ -42,19 +34,17 @@
H160(out)
}
+/*
fn call_internal<T: Config>(
collection: &mut CollectionHandle<T>,
caller: caller,
method_id: u32,
mut input: AbiReader,
value: U256,
-) -> Result<Option<AbiWriter>, evm_coder::abi::StringError> {
+) -> Result<Option<AbiWriter>> {
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(<CollectionHandle<T> 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<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {
fn is_reserved(target: &H160) -> bool {
@@ -129,56 +113,15 @@
) -> Option<PrecompileOutput> {
let mut collection = map_eth_to_id(target)
.and_then(|id| <CollectionHandle<T>>::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::<ethereum::Transaction>(&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::<UniqueNFTCall, _>(*source, &mut collection, value, input)
+ }
+ CollectionMode::Fungible(_) => {
+ call_internal::<UniqueFungibleCall, _>(*source, &mut collection, value, input)
+ }
+ _ => return None,
+ };
+ collection.recorder.evm_to_precompile_output(result)
}
}
pallets/nft/src/lib.rsdiffbeforeafterboth--- 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<T: Config> {
pub id: CollectionId,
collection: Collection<T>,
- logs: eth::log::LogRecorder,
- evm_address: H160,
- gas_limit: RefCell<u64>,
+ recorder: pallet_evm_coder_substrate::SubstrateRecorder<T>,
}
impl<T: Config> CollectionHandle<T> {
pub fn get_with_gas_limit(id: CollectionId, gas_limit: u64) -> Option<Self> {
<CollectionById<T>>::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> {
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::<T>::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<T> {
- self.collection
+ pub fn save(self) -> DispatchResult {
+ self.recorder.submit_logs()?;
+ <CollectionById<T>>::insert(self.id, self.collection);
+ Ok(())
}
}
impl<T: Config> Deref for CollectionHandle<T> {
@@ -230,7 +226,7 @@
}
}
-pub trait Config: system::Config + Sized {
+pub trait Config: system::Config + pallet_evm_coder_substrate::Config + Sized {
type Event: From<Event<Self>> + Into<<Self as system::Config>::Event>;
/// Weight information for extrinsics in this pallet.
@@ -245,9 +241,6 @@
<<Self as Config>::Currency as Currency<Self::AccountId>>::Balance,
>;
type TreasuryAccountId: Get<Self::AccountId>;
-
- type EthereumChainId: Get<u64>;
- type EthereumTransactionSender: pallet_ethereum::EthereumTransactionSender;
}
// # Used definitions
@@ -564,23 +557,23 @@
fail!(Error::<T>::NoPermission);
}
- <AddressTokens<T>>::remove_prefix(collection_id);
- <Allowances<T>>::remove_prefix(collection_id);
- <Balance<T>>::remove_prefix(collection_id);
+ <AddressTokens<T>>::remove_prefix(collection_id, None);
+ <Allowances<T>>::remove_prefix(collection_id, None);
+ <Balance<T>>::remove_prefix(collection_id, None);
<ItemListIndex>::remove(collection_id);
<AdminList<T>>::remove(collection_id);
<CollectionById<T>>::remove(collection_id);
- <WhiteList<T>>::remove_prefix(collection_id);
+ <WhiteList<T>>::remove_prefix(collection_id, None);
- <NftItemList<T>>::remove_prefix(collection_id);
- <FungibleItemList<T>>::remove_prefix(collection_id);
- <ReFungibleItemList<T>>::remove_prefix(collection_id);
+ <NftItemList<T>>::remove_prefix(collection_id, None);
+ <FungibleItemList<T>>::remove_prefix(collection_id, None);
+ <ReFungibleItemList<T>>::remove_prefix(collection_id, None);
- <NftTransferBasket<T>>::remove_prefix(collection_id);
- <FungibleTransferBasket<T>>::remove_prefix(collection_id);
- <ReFungibleTransferBasket<T>>::remove_prefix(collection_id);
+ <NftTransferBasket<T>>::remove_prefix(collection_id, None);
+ <FungibleTransferBasket<T>>::remove_prefix(collection_id, None);
+ <ReFungibleTransferBasket<T>>::remove_prefix(collection_id, None);
- <VariableMetaDataBasket<T>>::remove_prefix(collection_id);
+ <VariableMetaDataBasket<T>>::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<CollectionHandle<T>, sp_runtime::DispatchError> {
Ok(<CollectionHandle<T>>::get(collection_id).ok_or(Error::<T>::CollectionNotFound)?)
- }
-
- fn save_collection(collection: CollectionHandle<T>) {
- <CollectionById<T>>::insert(collection.id, collection.into_inner());
}
- pub fn submit_logs(collection: CollectionHandle<T>) -> 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<T>,
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,
pallets/nft/src/mock.rsdiffbeforeafterboth--- 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;
}