difftreelog
CORE-317 Fix ERC165 support interface
in: master
7 files changed
crates/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(
crates/evm-coder-macros/src/to_log.rsdiffbeforeafterboth1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use inflector::cases;18use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};19use std::fmt::Write;20use quote::quote;2122use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};2324struct EventField {25 name: Ident,26 camel_name: String,27 ty: Ident,28 indexed: bool,29}3031impl EventField {32 fn try_from(field: &Field) -> syn::Result<Self> {33 let name = field.ident.as_ref().unwrap();34 let ty = parse_ident_from_type(&field.ty, false)?;35 let mut indexed = false;36 for attr in &field.attrs {37 if let Ok(ident) = parse_ident_from_path(&attr.path, false) {38 if ident == "indexed" {39 indexed = true;40 }41 }42 }43 Ok(Self {44 name: name.to_owned(),45 camel_name: cases::camelcase::to_camel_case(&name.to_string()),46 ty: ty.to_owned(),47 indexed,48 })49 }50 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {51 let camel_name = &self.camel_name;52 let ty = &self.ty;53 let indexed = self.indexed;54 quote! {55 <SolidityEventArgument<#ty>>::new(#indexed, #camel_name)56 }57 }58}5960struct Event {61 name: Ident,62 name_screaming: Ident,63 fields: Vec<EventField>,64 selector: [u8; 32],65 selector_str: String,66}6768impl Event {69 fn try_from(variant: &Variant) -> syn::Result<Self> {70 let name = &variant.ident;71 let name_screaming = snake_ident_to_screaming(name);7273 let named = match &variant.fields {74 Fields::Named(named) => named,75 _ => {76 return Err(syn::Error::new(77 variant.fields.span(),78 "expected named fields",79 ))80 }81 };82 let mut fields = Vec::new();83 for field in &named.named {84 fields.push(EventField::try_from(field)?);85 }86 if fields.iter().filter(|f| f.indexed).count() > 3 {87 return Err(syn::Error::new(88 variant.fields.span(),89 "events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"90 ));91 }92 let mut selector_str = format!("{}(", name);93 for (i, arg) in fields.iter().enumerate() {94 if i != 0 {95 write!(selector_str, ",").unwrap();96 }97 write!(selector_str, "{}", arg.ty).unwrap();98 }99 selector_str.push(')');100 let selector = crate::event_selector_str(&selector_str);101102 Ok(Self {103 name: name.to_owned(),104 name_screaming,105 fields,106 selector,107 selector_str,108 })109 }110111 fn expand_serializers(&self) -> proc_macro2::TokenStream {112 let name = &self.name;113 let name_screaming = &self.name_screaming;114 let fields = self.fields.iter().map(|f| &f.name);115116 let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);117 let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);118119 quote! {120 Self::#name {#(121 #fields,122 )*} => {123 topics.push(topic::from(Self::#name_screaming));124 #(125 topics.push(#indexed.to_topic());126 )*127 #(128 #plain.abi_write(&mut writer);129 )*130 }131 }132 }133134 fn expand_consts(&self) -> proc_macro2::TokenStream {135 let name_screaming = &self.name_screaming;136 let selector_str = &self.selector_str;137 let selector = &self.selector;138139 quote! {140 #[doc = #selector_str]141 const #name_screaming: [u8; 32] = [#(142 #selector,143 )*];144 }145 }146147 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {148 let name = self.name.to_string();149 let args = self.fields.iter().map(EventField::expand_solidity_argument);150 quote! {151 SolidityEvent {152 name: #name,153 args: (154 #(155 #args,156 )*157 ),158 }159 }160 }161}162163pub struct Events {164 name: Ident,165 events: Vec<Event>,166}167168impl Events {169 pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {170 let name = &data.ident;171 let en = match &data.data {172 Data::Enum(en) => en,173 _ => return Err(syn::Error::new(data.span(), "expected enum")),174 };175 let mut events = Vec::new();176 for variant in &en.variants {177 events.push(Event::try_from(variant)?);178 }179 Ok(Self {180 name: name.to_owned(),181 events,182 })183 }184 pub fn expand(&self) -> proc_macro2::TokenStream {185 let name = &self.name;186187 let consts = self.events.iter().map(Event::expand_consts);188 let serializers = self.events.iter().map(Event::expand_serializers);189 let solidity_name = self.name.to_string();190 let solidity_functions = self.events.iter().map(Event::expand_solidity_function);191192 quote! {193 impl #name {194 #(195 #consts196 )*197198 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {199 use evm_coder::solidity::*;200 use core::fmt::Write;201 let interface = SolidityInterface {202 selector: 0,203 name: #solidity_name,204 is: &[],205 functions: (#(206 #solidity_functions,207 )*),208 };209 let mut out = string::new();210 out.push_str("// Inline\n");211 let _ = interface.format(is_impl, &mut out, tc);212 tc.collect(out);213 }214 }215216 #[automatically_derived]217 impl ::evm_coder::events::ToLog for #name {218 fn to_log(&self, contract: address) -> ::ethereum::Log {219 use ::evm_coder::events::ToTopic;220 use ::evm_coder::abi::AbiWrite;221 let mut writer = ::evm_coder::abi::AbiWriter::new();222 let mut topics = Vec::new();223 match self {224 #(225 #serializers,226 )*227 }228 ::ethereum::Log {229 address: contract,230 topics,231 data: writer.finish(),232 }233 }234 }235 }236 }237}1// Copyright 2019-2022 Unique Network (Gibraltar) Ltd.2// This file is part of Unique Network.34// Unique Network is free software: you can redistribute it and/or modify5// it under the terms of the GNU General Public License as published by6// the Free Software Foundation, either version 3 of the License, or7// (at your option) any later version.89// Unique Network is distributed in the hope that it will be useful,10// but WITHOUT ANY WARRANTY; without even the implied warranty of11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the12// GNU General Public License for more details.1314// You should have received a copy of the GNU General Public License15// along with Unique Network. If not, see <http://www.gnu.org/licenses/>.1617use inflector::cases;18use syn::{Data, DeriveInput, Field, Fields, Ident, Variant, spanned::Spanned};19use std::fmt::Write;20use quote::quote;2122use crate::{parse_ident_from_path, parse_ident_from_type, snake_ident_to_screaming};2324struct EventField {25 name: Ident,26 camel_name: String,27 ty: Ident,28 indexed: bool,29}3031impl EventField {32 fn try_from(field: &Field) -> syn::Result<Self> {33 let name = field.ident.as_ref().unwrap();34 let ty = parse_ident_from_type(&field.ty, false)?;35 let mut indexed = false;36 for attr in &field.attrs {37 if let Ok(ident) = parse_ident_from_path(&attr.path, false) {38 if ident == "indexed" {39 indexed = true;40 }41 }42 }43 Ok(Self {44 name: name.to_owned(),45 camel_name: cases::camelcase::to_camel_case(&name.to_string()),46 ty: ty.to_owned(),47 indexed,48 })49 }50 fn expand_solidity_argument(&self) -> proc_macro2::TokenStream {51 let camel_name = &self.camel_name;52 let ty = &self.ty;53 let indexed = self.indexed;54 quote! {55 <SolidityEventArgument<#ty>>::new(#indexed, #camel_name)56 }57 }58}5960struct Event {61 name: Ident,62 name_screaming: Ident,63 fields: Vec<EventField>,64 selector: [u8; 32],65 selector_str: String,66}6768impl Event {69 fn try_from(variant: &Variant) -> syn::Result<Self> {70 let name = &variant.ident;71 let name_screaming = snake_ident_to_screaming(name);7273 let named = match &variant.fields {74 Fields::Named(named) => named,75 _ => {76 return Err(syn::Error::new(77 variant.fields.span(),78 "expected named fields",79 ))80 }81 };82 let mut fields = Vec::new();83 for field in &named.named {84 fields.push(EventField::try_from(field)?);85 }86 if fields.iter().filter(|f| f.indexed).count() > 3 {87 return Err(syn::Error::new(88 variant.fields.span(),89 "events can have at most 4 indexed fields (1 indexed field is reserved for event signature)"90 ));91 }92 let mut selector_str = format!("{}(", name);93 for (i, arg) in fields.iter().enumerate() {94 if i != 0 {95 write!(selector_str, ",").unwrap();96 }97 write!(selector_str, "{}", arg.ty).unwrap();98 }99 selector_str.push(')');100 let selector = crate::event_selector_str(&selector_str);101102 Ok(Self {103 name: name.to_owned(),104 name_screaming,105 fields,106 selector,107 selector_str,108 })109 }110111 fn expand_serializers(&self) -> proc_macro2::TokenStream {112 let name = &self.name;113 let name_screaming = &self.name_screaming;114 let fields = self.fields.iter().map(|f| &f.name);115116 let indexed = self.fields.iter().filter(|f| f.indexed).map(|f| &f.name);117 let plain = self.fields.iter().filter(|f| !f.indexed).map(|f| &f.name);118119 quote! {120 Self::#name {#(121 #fields,122 )*} => {123 topics.push(topic::from(Self::#name_screaming));124 #(125 topics.push(#indexed.to_topic());126 )*127 #(128 #plain.abi_write(&mut writer);129 )*130 }131 }132 }133134 fn expand_consts(&self) -> proc_macro2::TokenStream {135 let name_screaming = &self.name_screaming;136 let selector_str = &self.selector_str;137 let selector = &self.selector;138139 quote! {140 #[doc = #selector_str]141 const #name_screaming: [u8; 32] = [#(142 #selector,143 )*];144 }145 }146147 fn expand_solidity_function(&self) -> proc_macro2::TokenStream {148 let name = self.name.to_string();149 let args = self.fields.iter().map(EventField::expand_solidity_argument);150 quote! {151 SolidityEvent {152 name: #name,153 args: (154 #(155 #args,156 )*157 ),158 }159 }160 }161}162163pub struct Events {164 name: Ident,165 events: Vec<Event>,166}167168impl Events {169 pub fn try_from(data: &DeriveInput) -> syn::Result<Self> {170 let name = &data.ident;171 let en = match &data.data {172 Data::Enum(en) => en,173 _ => return Err(syn::Error::new(data.span(), "expected enum")),174 };175 let mut events = Vec::new();176 for variant in &en.variants {177 events.push(Event::try_from(variant)?);178 }179 Ok(Self {180 name: name.to_owned(),181 events,182 })183 }184 pub fn expand(&self) -> proc_macro2::TokenStream {185 let name = &self.name;186187 let consts = self.events.iter().map(Event::expand_consts);188 let serializers = self.events.iter().map(Event::expand_serializers);189 let solidity_name = self.name.to_string();190 let solidity_functions = self.events.iter().map(Event::expand_solidity_function);191192 quote! {193 impl #name {194 #(195 #consts196 )*197198 pub fn generate_solidity_interface(tc: &evm_coder::solidity::TypeCollector, is_impl: bool) {199 use evm_coder::solidity::*;200 use core::fmt::Write;201 let interface = SolidityInterface {202 selector: [0; 4],203 name: #solidity_name,204 is: &[],205 functions: (#(206 #solidity_functions,207 )*),208 };209 let mut out = string::new();210 out.push_str("// Inline\n");211 let _ = interface.format(is_impl, &mut out, tc);212 tc.collect(out);213 }214 }215216 #[automatically_derived]217 impl ::evm_coder::events::ToLog for #name {218 fn to_log(&self, contract: address) -> ::ethereum::Log {219 use ::evm_coder::events::ToTopic;220 use ::evm_coder::abi::AbiWrite;221 let mut writer = ::evm_coder::abi::AbiWriter::new();222 let mut topics = Vec::new();223 match self {224 #(225 #serializers,226 )*227 }228 ::ethereum::Log {229 address: contract,230 topics,231 data: writer.finish(),232 }233 }234 }235 }236 }237}crates/evm-coder/src/abi.rsdiffbeforeafterboth--- a/crates/evm-coder/src/abi.rs
+++ b/crates/evm-coder/src/abi.rs
@@ -26,7 +26,7 @@
use crate::{
execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},
- types::string,
+ types::{string, self},
};
use crate::execution::Result;
@@ -46,7 +46,7 @@
offset: 0,
}
}
- pub fn new_call(buf: &'i [u8]) -> Result<(u32, Self)> {
+ pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {
if buf.len() < 4 {
return Err(Error::Error(ExitError::OutOfOffset));
}
@@ -54,7 +54,7 @@
method_id.copy_from_slice(&buf[0..4]);
Ok((
- u32::from_be_bytes(method_id),
+ method_id,
Self {
buf,
subresult_offset: 4,
@@ -63,25 +63,50 @@
))
}
- fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {
- if self.buf.len() - self.offset < ABI_ALIGNMENT {
+ 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]> {
+ if buf.len() - offset < ABI_ALIGNMENT {
return Err(Error::Error(ExitError::OutOfOffset));
}
let mut block = [0; S];
// Verify padding is empty
- if !self.buf[self.offset..self.offset + ABI_ALIGNMENT - S]
+ if !buf[pad_start..pad_size]
.iter()
.all(|&v| v == 0)
{
return Err(Error::Error(ExitError::InvalidRange));
}
block.copy_from_slice(
- &self.buf[self.offset + ABI_ALIGNMENT - S..self.offset + ABI_ALIGNMENT],
+ &buf[block_start..block_size],
);
+ Ok(block)
+ }
+
+ fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {
+ let offset = self.offset;
self.offset += ABI_ALIGNMENT;
- Ok(block)
+ Self::read_pad(
+ self.buf,
+ offset,
+ offset,
+ offset + ABI_ALIGNMENT - S,
+ offset + ABI_ALIGNMENT - S,
+ offset + ABI_ALIGNMENT
+ )
}
+ fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {
+ let offset = self.offset;
+ self.offset += ABI_ALIGNMENT;
+ Self::read_pad(
+ self.buf,
+ offset,
+ offset + S,
+ offset + ABI_ALIGNMENT,
+ offset,
+ offset + S
+ )
+ }
+
pub fn address(&mut self) -> Result<H160> {
Ok(H160(self.read_padleft()?))
}
@@ -96,7 +121,7 @@
}
pub fn bytes4(&mut self) -> Result<[u8; 4]> {
- self.read_padleft()
+ self.read_padright()
}
pub fn bytes(&mut self) -> Result<Vec<u8>> {
@@ -268,6 +293,7 @@
impl_abi_readable!(u64, uint64);
impl_abi_readable!(u128, uint128);
impl_abi_readable!(U256, uint256);
+impl_abi_readable!([u8; 4], bytes4);
impl_abi_readable!(H160, address);
impl_abi_readable!(Vec<u8>, bytes);
impl_abi_readable!(bool, bool);
@@ -466,7 +492,7 @@
"
))
.unwrap();
- assert_eq!(call, 0x50bb4e7f);
+ assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));
assert_eq!(
format!("{:?}", decoder.address().unwrap()),
"0xad2c0954693c2b5404b7e50967d3481bea432374"
@@ -505,7 +531,7 @@
"
))
.unwrap();
- assert_eq!(call, 0x36543006);
+ assert_eq!(call, u32::to_be_bytes(0x36543006));
let _ = decoder.address().unwrap();
let data =
<AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();
crates/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);
}
crates/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 ")?;
tests/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;
+ });
+});
tests/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",