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.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: (#(
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.jsondiffbeforeafterboth1[2 {3 "anonymous": false,4 "inputs": [5 {6 "indexed": true,7 "internalType": "address",8 "name": "owner",9 "type": "address"10 },11 {12 "indexed": true,13 "internalType": "address",14 "name": "approved",15 "type": "address"16 },17 {18 "indexed": true,19 "internalType": "uint256",20 "name": "tokenId",21 "type": "uint256"22 }23 ],24 "name": "Approval",25 "type": "event"26 },27 {28 "anonymous": false,29 "inputs": [30 {31 "indexed": true,32 "internalType": "address",33 "name": "owner",34 "type": "address"35 },36 {37 "indexed": true,38 "internalType": "address",39 "name": "operator",40 "type": "address"41 },42 {43 "indexed": false,44 "internalType": "bool",45 "name": "approved",46 "type": "bool"47 }48 ],49 "name": "ApprovalForAll",50 "type": "event"51 },52 {53 "anonymous": false,54 "inputs": [],55 "name": "MintingFinished",56 "type": "event"57 },58 {59 "anonymous": false,60 "inputs": [61 {62 "indexed": true,63 "internalType": "address",64 "name": "from",65 "type": "address"66 },67 {68 "indexed": true,69 "internalType": "address",70 "name": "to",71 "type": "address"72 },73 {74 "indexed": true,75 "internalType": "uint256",76 "name": "tokenId",77 "type": "uint256"78 }79 ],80 "name": "Transfer",81 "type": "event"82 },83 {84 "inputs": [85 {86 "internalType": "address",87 "name": "approved",88 "type": "address"89 },90 {91 "internalType": "uint256",92 "name": "tokenId",93 "type": "uint256"94 }95 ],96 "name": "approve",97 "outputs": [],98 "stateMutability": "nonpayable",99 "type": "function"100 },101 {102 "inputs": [103 {104 "internalType": "address",105 "name": "owner",106 "type": "address"107 }108 ],109 "name": "balanceOf",110 "outputs": [111 {112 "internalType": "uint256",113 "name": "",114 "type": "uint256"115 }116 ],117 "stateMutability": "view",118 "type": "function"119 },120 {121 "inputs": [122 {123 "internalType": "uint256",124 "name": "tokenId",125 "type": "uint256"126 }127 ],128 "name": "burn",129 "outputs": [],130 "stateMutability": "nonpayable",131 "type": "function"132 },133 {134 "inputs": [],135 "name": "finishMinting",136 "outputs": [137 {138 "internalType": "bool",139 "name": "",140 "type": "bool"141 }142 ],143 "stateMutability": "nonpayable",144 "type": "function"145 },146 {147 "inputs": [148 {149 "internalType": "uint256",150 "name": "tokenId",151 "type": "uint256"152 }153 ],154 "name": "getApproved",155 "outputs": [156 {157 "internalType": "address",158 "name": "",159 "type": "address"160 }161 ],162 "stateMutability": "view",163 "type": "function"164 },165 {166 "inputs": [167 {168 "internalType": "uint256",169 "name": "tokenId",170 "type": "uint256"171 }172 ],173 "name": "getVariableMetadata",174 "outputs": [175 {176 "internalType": "bytes",177 "name": "",178 "type": "bytes"179 }180 ],181 "stateMutability": "view",182 "type": "function"183 },184 {185 "inputs": [186 {187 "internalType": "address",188 "name": "owner",189 "type": "address"190 },191 {192 "internalType": "address",193 "name": "operator",194 "type": "address"195 }196 ],197 "name": "isApprovedForAll",198 "outputs": [199 {200 "internalType": "address",201 "name": "",202 "type": "address"203 }204 ],205 "stateMutability": "view",206 "type": "function"207 },208 {209 "inputs": [210 {211 "internalType": "address",212 "name": "to",213 "type": "address"214 },215 {216 "internalType": "uint256",217 "name": "tokenId",218 "type": "uint256"219 }220 ],221 "name": "mint",222 "outputs": [223 {224 "internalType": "bool",225 "name": "",226 "type": "bool"227 }228 ],229 "stateMutability": "nonpayable",230 "type": "function"231 },232 {233 "inputs": [234 {235 "internalType": "address",236 "name": "to",237 "type": "address"238 },239 {240 "internalType": "uint256[]",241 "name": "tokenIds",242 "type": "uint256[]"243 }244 ],245 "name": "mintBulk",246 "outputs": [247 {248 "internalType": "bool",249 "name": "",250 "type": "bool"251 }252 ],253 "stateMutability": "nonpayable",254 "type": "function"255 },256 {257 "inputs": [258 {259 "internalType": "address",260 "name": "to",261 "type": "address"262 },263 {264 "components": [265 {266 "internalType": "uint256",267 "name": "field_0",268 "type": "uint256"269 },270 {271 "internalType": "string",272 "name": "field_1",273 "type": "string"274 }275 ],276 "internalType": "struct Tuple0[]",277 "name": "tokens",278 "type": "tuple[]"279 }280 ],281 "name": "mintBulkWithTokenURI",282 "outputs": [283 {284 "internalType": "bool",285 "name": "",286 "type": "bool"287 }288 ],289 "stateMutability": "nonpayable",290 "type": "function"291 },292 {293 "inputs": [294 {295 "internalType": "address",296 "name": "to",297 "type": "address"298 },299 {300 "internalType": "uint256",301 "name": "tokenId",302 "type": "uint256"303 },304 {305 "internalType": "string",306 "name": "tokenUri",307 "type": "string"308 }309 ],310 "name": "mintWithTokenURI",311 "outputs": [312 {313 "internalType": "bool",314 "name": "",315 "type": "bool"316 }317 ],318 "stateMutability": "nonpayable",319 "type": "function"320 },321 {322 "inputs": [],323 "name": "mintingFinished",324 "outputs": [325 {326 "internalType": "bool",327 "name": "",328 "type": "bool"329 }330 ],331 "stateMutability": "view",332 "type": "function"333 },334 {335 "inputs": [],336 "name": "name",337 "outputs": [338 {339 "internalType": "string",340 "name": "",341 "type": "string"342 }343 ],344 "stateMutability": "view",345 "type": "function"346 },347 {348 "inputs": [],349 "name": "nextTokenId",350 "outputs": [351 {352 "internalType": "uint256",353 "name": "",354 "type": "uint256"355 }356 ],357 "stateMutability": "view",358 "type": "function"359 },360 {361 "inputs": [362 {363 "internalType": "uint256",364 "name": "tokenId",365 "type": "uint256"366 }367 ],368 "name": "ownerOf",369 "outputs": [370 {371 "internalType": "address",372 "name": "",373 "type": "address"374 }375 ],376 "stateMutability": "view",377 "type": "function"378 },379 {380 "inputs": [381 {382 "internalType": "address",383 "name": "from",384 "type": "address"385 },386 {387 "internalType": "address",388 "name": "to",389 "type": "address"390 },391 {392 "internalType": "uint256",393 "name": "tokenId",394 "type": "uint256"395 }396 ],397 "name": "safeTransferFrom",398 "outputs": [],399 "stateMutability": "nonpayable",400 "type": "function"401 },402 {403 "inputs": [404 {405 "internalType": "address",406 "name": "from",407 "type": "address"408 },409 {410 "internalType": "address",411 "name": "to",412 "type": "address"413 },414 {415 "internalType": "uint256",416 "name": "tokenId",417 "type": "uint256"418 },419 {420 "internalType": "bytes",421 "name": "data",422 "type": "bytes"423 }424 ],425 "name": "safeTransferFromWithData",426 "outputs": [],427 "stateMutability": "nonpayable",428 "type": "function"429 },430 {431 "inputs": [432 {433 "internalType": "address",434 "name": "operator",435 "type": "address"436 },437 {438 "internalType": "bool",439 "name": "approved",440 "type": "bool"441 }442 ],443 "name": "setApprovalForAll",444 "outputs": [],445 "stateMutability": "nonpayable",446 "type": "function"447 },448 {449 "inputs": [450 {451 "internalType": "uint256",452 "name": "tokenId",453 "type": "uint256"454 },455 {456 "internalType": "bytes",457 "name": "data",458 "type": "bytes"459 }460 ],461 "name": "setVariableMetadata",462 "outputs": [],463 "stateMutability": "nonpayable",464 "type": "function"465 },466 {467 "inputs": [468 {469 "internalType": "uint32",470 "name": "interfaceId",471 "type": "uint32"472 }473 ],474 "name": "supportsInterface",475 "outputs": [476 {477 "internalType": "bool",478 "name": "",479 "type": "bool"480 }481 ],482 "stateMutability": "view",483 "type": "function"484 },485 {486 "inputs": [],487 "name": "symbol",488 "outputs": [489 {490 "internalType": "string",491 "name": "",492 "type": "string"493 }494 ],495 "stateMutability": "view",496 "type": "function"497 },498 {499 "inputs": [500 {501 "internalType": "uint256",502 "name": "index",503 "type": "uint256"504 }505 ],506 "name": "tokenByIndex",507 "outputs": [508 {509 "internalType": "uint256",510 "name": "",511 "type": "uint256"512 }513 ],514 "stateMutability": "view",515 "type": "function"516 },517 {518 "inputs": [519 {520 "internalType": "address",521 "name": "owner",522 "type": "address"523 },524 {525 "internalType": "uint256",526 "name": "index",527 "type": "uint256"528 }529 ],530 "name": "tokenOfOwnerByIndex",531 "outputs": [532 {533 "internalType": "uint256",534 "name": "",535 "type": "uint256"536 }537 ],538 "stateMutability": "view",539 "type": "function"540 },541 {542 "inputs": [543 {544 "internalType": "uint256",545 "name": "tokenId",546 "type": "uint256"547 }548 ],549 "name": "tokenURI",550 "outputs": [551 {552 "internalType": "string",553 "name": "",554 "type": "string"555 }556 ],557 "stateMutability": "view",558 "type": "function"559 },560 {561 "inputs": [],562 "name": "totalSupply",563 "outputs": [564 {565 "internalType": "uint256",566 "name": "",567 "type": "uint256"568 }569 ],570 "stateMutability": "view",571 "type": "function"572 },573 {574 "inputs": [575 {576 "internalType": "address",577 "name": "to",578 "type": "address"579 },580 {581 "internalType": "uint256",582 "name": "tokenId",583 "type": "uint256"584 }585 ],586 "name": "transfer",587 "outputs": [],588 "stateMutability": "nonpayable",589 "type": "function"590 },591 {592 "inputs": [593 {594 "internalType": "address",595 "name": "from",596 "type": "address"597 },598 {599 "internalType": "address",600 "name": "to",601 "type": "address"602 },603 {604 "internalType": "uint256",605 "name": "tokenId",606 "type": "uint256"607 }608 ],609 "name": "transferFrom",610 "outputs": [],611 "stateMutability": "nonpayable",612 "type": "function"613 }614]