git.delta.rocks / unique-network / refs/commits / 0902e70cd1b8

difftreelog

CORE-317 Fix ERC165 support interface

Trubnikov Sergey2022-04-11parent: #219f5f0.patch.diff
in: master

7 files changed

modifiedcrates/evm-coder-macros/src/solidity_interface.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/solidity_interface.rs
+++ b/crates/evm-coder-macros/src/solidity_interface.rs
@@ -57,7 +57,7 @@
 	fn expand_interface_id(&self) -> proc_macro2::TokenStream {
 		let pascal_call_name = &self.pascal_call_name;
 		quote! {
-			interface_id ^= #pascal_call_name::interface_id();
+			interface_id ^= u32::from_be_bytes(#pascal_call_name::interface_id());
 		}
 	}
 
@@ -540,18 +540,18 @@
 
 	fn expand_const(&self) -> proc_macro2::TokenStream {
 		let screaming_name = &self.screaming_name;
-		let selector = self.selector;
+		let selector = u32::to_be_bytes(self.selector);
 		let selector_str = &self.selector_str;
 		quote! {
 			#[doc = #selector_str]
-			const #screaming_name: u32 = #selector;
+			const #screaming_name: ::evm_coder::types::bytes4 = [#(#selector,)*];
 		}
 	}
 
 	fn expand_interface_id(&self) -> proc_macro2::TokenStream {
 		let screaming_name = &self.screaming_name;
 		quote! {
-			interface_id ^= Self::#screaming_name;
+			interface_id ^= u32::from_be_bytes(Self::#screaming_name);
 		}
 	}
 
@@ -831,14 +831,14 @@
 				#(
 					#consts
 				)*
-				pub fn interface_id() -> u32 {
+				pub fn interface_id() -> ::evm_coder::types::bytes4 {
 					let mut interface_id = 0;
 					#(#interface_id)*
 					#(#inline_interface_id)*
-					interface_id
+					u32::to_be_bytes(interface_id)
 				}
-				pub fn supports_interface(interface_id: u32) -> bool {
-					interface_id != 0xffffff && (
+				pub fn supports_interface(interface_id: ::evm_coder::types::bytes4) -> bool {
+					interface_id != u32::to_be_bytes(0xffffff) && (
 						interface_id == ::evm_coder::ERC165Call::INTERFACE_ID ||
 						interface_id == Self::interface_id()
 						#(
@@ -884,7 +884,7 @@
 				}
 			}
 			impl #gen_ref ::evm_coder::Call for #call_name #gen_ref {
-				fn parse(method_id: u32, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
+				fn parse(method_id: ::evm_coder::types::bytes4, reader: &mut ::evm_coder::abi::AbiReader) -> ::evm_coder::execution::Result<Option<Self>> {
 					use ::evm_coder::abi::AbiRead;
 					match method_id {
 						::evm_coder::ERC165Call::INTERFACE_ID => return Ok(
modifiedcrates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth
--- a/crates/evm-coder-macros/src/to_log.rs
+++ b/crates/evm-coder-macros/src/to_log.rs
@@ -199,7 +199,7 @@
 					use evm_coder::solidity::*;
 					use core::fmt::Write;
 					let interface = SolidityInterface {
-						selector: 0,
+						selector: [0; 4],
 						name: #solidity_name,
 						is: &[],
 						functions: (#(
modifiedcrates/evm-coder/src/abi.rsdiffbeforeafterboth
2626
27use crate::{27use crate::{
28 execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},28 execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},
29 types::string,29 types::{string, self},
30};30};
31use crate::execution::Result;31use crate::execution::Result;
3232
46 offset: 0,46 offset: 0,
47 }47 }
48 }48 }
49 pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> {49 pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {
50 if buf.len() < 4 {50 if buf.len() < 4 {
51 return Err(Error::Error(ExitError::OutOfOffset));51 return Err(Error::Error(ExitError::OutOfOffset));
52 }52 }
53 let mut method_id = [0; 4];53 let mut method_id = [0; 4];
54 method_id.copy_from_slice(&buf[0..4]);54 method_id.copy_from_slice(&buf[0..4]);
5555
56 Ok((56 Ok((
57 u32::from_be_bytes(method_id),57 method_id,
58 Self {58 Self {
59 buf,59 buf,
60 subresult_offset: 4,60 subresult_offset: 4,
63 ))63 ))
64 }64 }
6565
66 fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {66 fn read_pad<const S: usize>(buf: &[u8], offset: usize, pad_start: usize, pad_size: usize, block_start: usize, block_size: usize) -> Result<[u8; S]> {
67 if self.buf.len() - self.offset < ABI_ALIGNMENT {67 if buf.len() - offset < ABI_ALIGNMENT {
68 return Err(Error::Error(ExitError::OutOfOffset));68 return Err(Error::Error(ExitError::OutOfOffset));
69 }69 }
70 let mut block = [0; S];70 let mut block = [0; S];
71 // Verify padding is empty71 // Verify padding is empty
72 if !self.buf[self.offset..self.offset + ABI_ALIGNMENT - S]72 if !buf[pad_start..pad_size]
73 .iter()73 .iter()
74 .all(|&v| v == 0)74 .all(|&v| v == 0)
75 {75 {
76 return Err(Error::Error(ExitError::InvalidRange));76 return Err(Error::Error(ExitError::InvalidRange));
77 }77 }
78 block.copy_from_slice(78 block.copy_from_slice(
79 &self.buf[self.offset + ABI_ALIGNMENT - S..self.offset + ABI_ALIGNMENT],79 &buf[block_start..block_size],
80 );80 );
81 self.offset += ABI_ALIGNMENT;
82 Ok(block)81 Ok(block)
83 }82 }
8483
84 fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {
85 let offset = self.offset;
86 self.offset += ABI_ALIGNMENT;
87 Self::read_pad(
88 self.buf,
89 offset,
90 offset,
91 offset + ABI_ALIGNMENT - S,
92 offset + ABI_ALIGNMENT - S,
93 offset + ABI_ALIGNMENT
94 )
95 }
96
97 fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {
98 let offset = self.offset;
99 self.offset += ABI_ALIGNMENT;
100 Self::read_pad(
101 self.buf,
102 offset,
103 offset + S,
104 offset + ABI_ALIGNMENT,
105 offset,
106 offset + S
107 )
108 }
109
85 pub fn address(&mut self) -> Result<H160> {110 pub fn address(&mut self) -> Result<H160> {
86 Ok(H160(self.read_padleft()?))111 Ok(H160(self.read_padleft()?))
96 }121 }
97122
98 pub fn bytes4(&mut self) -> Result<[u8; 4]> {123 pub fn bytes4(&mut self) -> Result<[u8; 4]> {
99 self.read_padleft()124 self.read_padright()
100 }125 }
101126
102 pub fn bytes(&mut self) -> Result<Vec<u8>> {127 pub fn bytes(&mut self) -> Result<Vec<u8>> {
268impl_abi_readable!(u64, uint64);293impl_abi_readable!(u64, uint64);
269impl_abi_readable!(u128, uint128);294impl_abi_readable!(u128, uint128);
270impl_abi_readable!(U256, uint256);295impl_abi_readable!(U256, uint256);
296impl_abi_readable!([u8; 4], bytes4);
271impl_abi_readable!(H160, address);297impl_abi_readable!(H160, address);
272impl_abi_readable!(Vec<u8>, bytes);298impl_abi_readable!(Vec<u8>, bytes);
273impl_abi_readable!(bool, bool);299impl_abi_readable!(bool, bool);
466 "492 "
467 ))493 ))
468 .unwrap();494 .unwrap();
469 assert_eq!(call, 0x50bb4e7f);495 assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));
470 assert_eq!(496 assert_eq!(
471 format!("{:?}", decoder.address().unwrap()),497 format!("{:?}", decoder.address().unwrap()),
472 "0xad2c0954693c2b5404b7e50967d3481bea432374"498 "0xad2c0954693c2b5404b7e50967d3481bea432374"
505 "531 "
506 ))532 ))
507 .unwrap();533 .unwrap();
508 assert_eq!(call, 0x36543006);534 assert_eq!(call, u32::to_be_bytes(0x36543006));
509 let _ = decoder.address().unwrap();535 let _ = decoder.address().unwrap();
510 let data =536 let data =
511 <AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();537 <AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();
modifiedcrates/evm-coder/src/lib.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/lib.rs
+++ b/crates/evm-coder/src/lib.rs
@@ -44,7 +44,7 @@
 	pub type uint128 = u128;
 	pub type uint256 = U256;
 
-	pub type bytes4 = u32;
+	pub type bytes4 = [u8; 4];
 
 	pub type topic = H256;
 
@@ -71,7 +71,7 @@
 }
 
 pub trait Call: Sized {
-	fn parse(selector: u32, input: &mut AbiReader) -> execution::Result<Option<Self>>;
+	fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>>;
 }
 
 pub type Weight = u64;
@@ -93,11 +93,11 @@
 }
 
 impl ERC165Call {
-	pub const INTERFACE_ID: types::bytes4 = 0x01ffc9a7;
+	pub const INTERFACE_ID: types::bytes4 = u32::to_be_bytes(0x01ffc9a7);
 }
 
 impl Call for ERC165Call {
-	fn parse(selector: u32, input: &mut AbiReader) -> execution::Result<Option<Self>> {
+	fn parse(selector: types::bytes4, input: &mut AbiReader) -> execution::Result<Option<Self>> {
 		if selector != Self::INTERFACE_ID {
 			return Ok(None);
 		}
modifiedcrates/evm-coder/src/solidity.rsdiffbeforeafterboth
--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -103,6 +103,7 @@
 	uint64 => "uint64" true = "0",
 	uint128 => "uint128" true = "0",
 	uint256 => "uint256" true = "0",
+	bytes4 => "bytes4" true = "bytes4(0)",
 	address => "address" true = "0x0000000000000000000000000000000000000000",
 	string => "string" false = "\"\"",
 	bytes => "bytes" false = "hex\"\"",
@@ -473,7 +474,7 @@
 }
 
 pub struct SolidityInterface<F: SolidityFunctions> {
-	pub selector: u32,
+	pub selector: bytes4,
 	pub name: &'static str,
 	pub is: &'static [&'static str],
 	pub functions: F,
@@ -486,8 +487,9 @@
 		out: &mut impl fmt::Write,
 		tc: &TypeCollector,
 	) -> fmt::Result {
-		if self.selector != 0 {
-			writeln!(out, "// Selector: {:0>8x}", self.selector)?;
+		const ZERO_BYTES: [u8; 4] = [0; 4];
+		if self.selector != ZERO_BYTES {
+			writeln!(out, "// Selector: {:0>8x}", u32::from_be_bytes(self.selector))?;
 		}
 		if is_impl {
 			write!(out, "contract ")?;
modifiedtests/src/eth/base.test.tsdiffbeforeafterboth
--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -14,11 +14,13 @@
 // You should have received a copy of the GNU General Public License
 // along with Unique Network. If not, see <http://www.gnu.org/licenses/>.
 
-import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, deployFlipper, ethBalanceViaSub, GAS_ARGS, itWeb3, recordEthFee} from './util/helpers';
+import {collectionIdToAddress, createEthAccount, createEthAccountWithBalance, deployFlipper, ethBalanceViaSub, GAS_ARGS, itWeb3, recordEthFee, usingWeb3} from './util/helpers';
 import {expect} from 'chai';
 import {createCollectionExpectSuccess, createItemExpectSuccess, UNIQUE} from '../util/helpers';
 import nonFungibleAbi from './nonFungibleAbi.json';
 import privateKey from '../substrate/privateKey';
+import {Contract} from 'web3-eth-contract';
+import Web3 from 'web3';
 
 describe('Contract calls', () => {
   itWeb3('Call of simple contract fee is less than 0.2 UNQ', async ({web3, api}) => {
@@ -59,3 +61,53 @@
     expect(Math.abs(fee - expectedFee)).to.be.lessThan(tolerance);
   });
 });
+
+describe('ERC165 tests', async () => {
+  // https://eips.ethereum.org/EIPS/eip-165
+
+  let collection: number;
+  let minter: string;
+
+  function contract(web3: Web3): Contract {
+    return new web3.eth.Contract(nonFungibleAbi as any, collectionIdToAddress(collection), {from: minter, ...GAS_ARGS});
+  }
+
+  before(async () => {
+    await usingWeb3 (async (web3) => {
+      collection = await createCollectionExpectSuccess();
+      minter = createEthAccount(web3);
+    });
+  });
+  
+  itWeb3('interfaceID == 0xffffffff always false', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0xffffffff').call()).to.be.false;
+  });
+
+  itWeb3('ERC721 support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0x58800161').call()).to.be.true;
+  });
+
+  itWeb3('ERC721Metadata support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0x5b5e139f').call()).to.be.true;
+  });
+
+  itWeb3('ERC721Mintable support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0x68ccfe89').call()).to.be.true;
+  });
+
+  itWeb3('ERC721Enumerable support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0x780e9d63').call()).to.be.true;
+  });
+
+  itWeb3('ERC721UniqueExtensions support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0xe562194d').call()).to.be.true;
+  });
+
+  itWeb3('ERC721Burnable support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0x42966c68').call()).to.be.true;
+  });
+
+  itWeb3('ERC165 support', async ({web3}) => {
+    expect(await contract(web3).methods.supportsInterface('0x01ffc9a7').call()).to.be.true;
+  });
+});
modifiedtests/src/eth/nonFungibleAbi.jsondiffbeforeafterboth
--- a/tests/src/eth/nonFungibleAbi.json
+++ b/tests/src/eth/nonFungibleAbi.json
@@ -466,9 +466,9 @@
     {
         "inputs": [
             {
-                "internalType": "uint32",
+                "internalType": "bytes4",
                 "name": "interfaceId",
-                "type": "uint32"
+                "type": "bytes4"
             }
         ],
         "name": "supportsInterface",