difftreelog
refactor switch to macro-based ERC impls
in: master
2 files changed
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().clone())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/mod.rsdiffbeforeafterboth--- a/pallets/nft/src/eth/mod.rs
+++ b/pallets/nft/src/eth/mod.rs
@@ -36,209 +36,63 @@
H160(out)
}
-fn result_to_output(result: Result<AbiWriter, Option<&'static str>>, logs: Vec<PrecompileLog>) -> PrecompileOutput {
- sp_io::storage::start_transaction();
- match result {
- Ok(result) => {
- sp_io::storage::commit_transaction();
- // TODO: weight
- PrecompileOutput(ExitReason::Succeed(ExitSucceed::Returned), result.finish(), 0, logs)
- }
- Err(Some(s)) => {
- sp_io::storage::rollback_transaction();
- // Error(string)
- let mut out = AbiWriter::new_call(0x08c379a0);
- out.string(&s);
- PrecompileOutput(ExitReason::Revert(ExitRevert::Reverted), out.finish(), 0, Vec::new())
- }
- Err(None) => {
- sp_io::storage::rollback_transaction();
- PrecompileOutput(ExitReason::Revert(ExitRevert::Reverted), Vec::new(), 0, Vec::new())
- }
- }
-}
-
-fn call_internal<T: Config>(sender: H160, collection: &CollectionHandle<T>, method_id: u32, mut input: AbiReader) -> Result<AbiWriter, Option<&'static str>> {
- let erc20 = matches!(collection.mode, CollectionMode::Fungible(_));
- let erc721 = matches!(collection.mode, CollectionMode::NFT);
-
- Ok(match method_id {
- // function name() external view returns (string memory)
- fn_selector!(name()) => {
- let name = collection.name.iter()
- .map(|&e| e.try_into().ok() as Option<u8>)
- .collect::<Option<Vec<u8>>>()
- .ok_or(Some("non-ascii name"))?;
-
- crate::abi_encode!(memory(&name))
- }
- // function symbol() external view returns (string memory)
- fn_selector!(symbol()) => {
- let name = collection.token_prefix.iter()
- .map(|&e| e.is_ascii_uppercase().then(|| e))
- .collect::<Option<Vec<u8>>>()
- .ok_or(Some("non-uppercase prefix"))?;
-
- crate::abi_encode!(memory(&name))
- }
- // function decimals() external view returns (uint8 decimals)
- fn_selector!(decimals()) if erc20 => {
- if let CollectionMode::Fungible(decimals) = &collection.mode {
- crate::abi_encode!(uint8(*decimals))
- } else {
- unreachable!()
+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> {
+ match collection.mode.clone() {
+ CollectionMode::Fungible(_) => {
+ #[cfg(feature = "std")]
+ {
+ println!("Parse fungible call {:x}", method_id);
}
- }
- // function totalSupply() external view returns (uint256)
- fn_selector!(totalSupply()) if erc20 || erc721 => {
- // TODO: can't be implemented, as we don't track total amount of fungibles
- crate::abi_encode!(uint256(0))
- }
- // function balanceOf(address account) external view returns (uint256)
- fn_selector!(balanceOf(address)) if erc20 || erc721 => {
- crate::abi_decode!(input, account: address);
- let account = T::EvmAddressMapping::into_account_id(account);
- let balance = <Balance<T>>::get(collection.id, account);
- crate::abi_encode!(uint256(balance))
+ let call = match UniqueFungibleCall::parse(method_id, &mut input)? {
+ Some(v) => v,
+ None => {
+ #[cfg(feature = "std")]
+ {
+ println!("Method not found");
+ }
+ return Ok(None);
+ }
+ };
+ #[cfg(feature = "std")]
+ {
+ dbg!(&call);
+ }
+ Ok(Some(<CollectionHandle<T> as UniqueFungible>::call(
+ collection,
+ Msg {
+ call,
+ caller,
+ value,
+ },
+ )?))
}
- // function ownerOf(uint256 tokenId) external view returns (address)
- fn_selector!(ownerOf(uint256)) if erc721 => {
- crate::abi_decode!(input, token_id: uint256);
- let token_id: u32 = token_id.try_into().map_err(|_| "bad token id")?;
-
- let token = <NftItemList<T>>::get(collection.id, token_id).ok_or("unknown token")?;
-
- crate::abi_encode!(address(token.owner.as_eth().clone()))
+ CollectionMode::NFT => {
+ let call = match UniqueNFTCall::parse(method_id, &mut input)? {
+ Some(v) => v,
+ None => return Ok(None),
+ };
+ Ok(Some(<CollectionHandle<T> as UniqueNFT>::call(
+ collection,
+ Msg {
+ call,
+ caller,
+ value,
+ },
+ )?))
}
- // function transfer(address recipient, uint256 amount) external returns (bool) {
- fn_selector!(transfer(address, uint256)) if erc20 => {
- crate::abi_decode!(input, recipient: address, amount: uint256);
- let sender = T::CrossAccountId::from_eth(sender);
- let recipient = T::CrossAccountId::from_eth(recipient);
-
- <Module<T>>::transfer_internal(
- &sender,
- &recipient,
- &collection,
- 1,
- amount,
- ).map_err(|_| "transfer error")?;
-
- crate::abi_encode!(bool(true))
+ _ => {
+ return Err(StringError::from(
+ "erc calls only supported to fungible and nft collections for now",
+ )
+ .into())
}
-
- fn_selector!(transfer(address, uint256)) if erc721 => {
- crate::abi_decode!(input, recipient: address, token_id: uint256);
- let sender = T::CrossAccountId::from_eth(sender);
- let recipient = T::CrossAccountId::from_eth(recipient);
- let token_id: u32 = token_id.try_into().map_err(|_| "bad token id")?;
-
- <Module<T>>::transfer_internal(
- &sender,
- &recipient,
- &collection,
- token_id,
- 1,
- ).map_err(|_| "transfer error")?;
-
- crate::abi_encode!(bool(true))
- }
-
- // function allowance(address owner, address spender) external view returns (uint256)
- fn_selector!(allowance(address, address)) if erc20 => {
- crate::abi_decode!(input, owner: address, spender: address);
- let owner = T::EvmAddressMapping::into_account_id(owner);
- let spender = T::EvmAddressMapping::into_account_id(spender);
- let allowance = <Allowances<T>>::get(collection.id, (1, &owner, &spender));
- crate::abi_encode!(uint256(allowance))
- }
- // function approve(address spender, uint256 amount) external returns (bool)
- // FIXME: All current implementations resets amount to specified value, ours - adds it
- // FIXME: Our implementation doesn't handle resets (approve with zero amount)
- fn_selector!(approve(address, uint256)) if erc20 => {
- crate::abi_decode!(input, spender: address, amount: uint256);
- let sender = T::CrossAccountId::from_eth(sender);
- let spender = T::CrossAccountId::from_eth(spender);
-
- <Module<T>>::approve_internal(
- &sender,
- &spender,
- &collection,
- 1,
- amount,
- ).map_err(|_| "approve error")?;
-
- crate::abi_encode!(bool(true))
- }
- // function approve(address approved, uint256 tokenId) external payable
- fn_selector!(approve(address, uint256)) if erc721 => {
- crate::abi_decode!(input, approved: address, token_id: uint256);
- let sender = T::CrossAccountId::from_eth(sender);
- let approved = T::CrossAccountId::from_eth(approved);
- let token_id = token_id.try_into().map_err(|_| "bad token id")?;
-
- <Module<T>>::approve_internal(
- &sender,
- &approved,
- &collection,
- token_id,
- 1,
- ).map_err(|_| "approve error")?;
- crate::abi_encode!()
- }
- // function transferFrom(address sender, address recipient, uint256 amount) external returns (bool)
- fn_selector!(transferFrom(address, address, uint256)) if erc20 => {
- crate::abi_decode!(input, from: address, recipient: address, amount: uint256);
- let sender = T::CrossAccountId::from_eth(sender);
- let from = T::CrossAccountId::from_eth(from);
- let recipient = T::CrossAccountId::from_eth(recipient);
-
- <Module<T>>::transfer_from_internal(
- &sender,
- &from,
- &recipient,
- &collection,
- 1,
- amount,
- ).map_err(|_| "transfer_from error")?;
-
- crate::abi_encode!(bool(true))
- }
- // function transferFrom(address from, address to, uint256 tokenId) external payable
- fn_selector!(transferFrom(address, address, uint256)) if erc721 => {
- crate::abi_decode!(input, from: address, recipient: address, token_id: uint256);
- let sender = T::CrossAccountId::from_eth(sender);
- let from = T::CrossAccountId::from_eth(from);
- let recipient = T::CrossAccountId::from_eth(recipient);
- let token_id = token_id.try_into().map_err(|_| "bad token id")?;
-
- <Module<T>>::transfer_from_internal(
- &sender,
- &from,
- &recipient,
- &collection,
- token_id,
- 1,
- ).map_err(|_| "transfer_from error")?;
-
- crate::abi_encode!()
- }
- // function supportsInterface(bytes4 interfaceID) public pure returns (bool)
- fn_selector!(supportsInterface(bytes4)) => {
- crate::abi_decode!(input, interface_id: uint32);
- let supports = match interface_id {
- // ERC165
- 0x01ffc9a7 => true,
- // ERC20
- 0x36372b07 if erc20 => true,
- // ERC721
- 0x80ac58cd if erc721 => true,
- _ => false,
- };
- crate::abi_encode!(bool(supports))
- }
- _ => return Err(None)
- })
+ }
}
impl<T: Config> pallet_evm::OnMethodCall<T> for NftErcSupport<T> {
@@ -265,35 +119,33 @@
fn call(
source: &H160,
target: &H160,
+ gas_limit: u64,
input: &[u8],
+ value: U256,
) -> Option<PrecompileOutput> {
- let collection = map_eth_to_id(&target)
- .and_then(<CollectionHandle<T>>::get)?;
+ 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(*source, &collection, method_id, input);
- Some(result_to_output(result, collection.logs.retrieve_logs_for_contract(*target)))
+ 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(),
+ }),
+ }
}
}
-
-pub const TRANSFER_NFT_TOPIC: H256 = event_topic!(Transfer(address, address, uint256));
-pub const APPROVAL_NFT_TOPIC: H256 = event_topic!(Approval(address, address, uint256));
-// TODO: event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
-
-pub const TRANSFER_FUNGIBLE_TOPIC: H256 = event_topic!(Transfer(address, address, uint256));
-pub const APPROVAL_FUNGIBLE_TOPIC: H256 = event_topic!(Approval(address, address, uint256));
-
-pub fn address_to_topic(address: &H160) -> H256 {
- let mut output = [0; 32];
- output[12..32].copy_from_slice(&address.0);
- H256(output)
-}
-
-pub fn u32_to_topic(id: u32) -> H256 {
- let mut output = [0; 32];
- output[28..32].copy_from_slice(&id.to_be_bytes());
- H256(output)
-}
-
// TODO: This function is slow, and output can be memoized
pub fn generate_transaction(collection_id: u32, chain_id: u64) -> ethereum::Transaction {