difftreelog
fix After rebase
in: master
8 files changed
crates/evm-coder/src/abi.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/>.1617//! Implementation of EVM RLP reader/writer1819#![allow(dead_code)]2021#[cfg(not(feature = "std"))]22use alloc::vec::Vec;23use evm_core::ExitError;24use primitive_types::{H160, U256};2526use crate::{27 execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},28 types::{string, self},29};30use crate::execution::Result;31use crate::solidity::SolidityTypeName;3233const ABI_ALIGNMENT: usize = 32;3435/// View into RLP data, which provides method to read typed items from it36#[derive(Clone)]37pub struct AbiReader<'i> {38 buf: &'i [u8],39 subresult_offset: usize,40 offset: usize,41}42impl<'i> AbiReader<'i> {43 /// Start reading RLP buffer, assuming there is no padding bytes44 pub fn new(buf: &'i [u8]) -> Self {45 Self {46 buf,47 subresult_offset: 0,48 offset: 0,49 }50 }51 /// Start reading RLP buffer, parsing first 4 bytes as selector52 pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {53 if buf.len() < 4 {54 return Err(Error::Error(ExitError::OutOfOffset));55 }56 let mut method_id = [0; 4];57 method_id.copy_from_slice(&buf[0..4]);5859 Ok((60 method_id,61 Self {62 buf,63 subresult_offset: 4,64 offset: 4,65 },66 ))67 }6869 fn read_pad<const S: usize>(70 buf: &[u8],71 offset: usize,72 pad_start: usize,73 pad_size: usize,74 block_start: usize,75 block_size: usize,76 ) -> Result<[u8; S]> {77 if buf.len() - offset < ABI_ALIGNMENT {78 return Err(Error::Error(ExitError::OutOfOffset));79 }80 let mut block = [0; S];81 let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0);82 if !is_pad_zeroed {83 return Err(Error::Error(ExitError::InvalidRange));84 }85 block.copy_from_slice(&buf[block_start..block_size]);86 Ok(block)87 }8889 fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {90 let offset = self.offset;91 self.offset += ABI_ALIGNMENT;92 Self::read_pad(93 self.buf,94 offset,95 offset,96 offset + ABI_ALIGNMENT - S,97 offset + ABI_ALIGNMENT - S,98 offset + ABI_ALIGNMENT,99 )100 }101102 fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {103 let offset = self.offset;104 self.offset += ABI_ALIGNMENT;105 Self::read_pad(106 self.buf,107 offset,108 offset + S,109 offset + ABI_ALIGNMENT,110 offset,111 offset + S,112 )113 }114115 /// Read [`H160`] at current position, then advance116 pub fn address(&mut self) -> Result<H160> {117 Ok(H160(self.read_padleft()?))118 }119120 /// Read [`bool`] at current position, then advance121 pub fn bool(&mut self) -> Result<bool> {122 let data: [u8; 1] = self.read_padleft()?;123 match data[0] {124 0 => Ok(false),125 1 => Ok(true),126 _ => Err(Error::Error(ExitError::InvalidRange)),127 }128 }129130 /// Read [`[u8; 4]`] at current position, then advance131 pub fn bytes4(&mut self) -> Result<[u8; 4]> {132 self.read_padright()133 }134135 /// Read [`Vec<u8>`] at current position, then advance136 pub fn bytes(&mut self) -> Result<Vec<u8>> {137 let mut subresult = self.subresult(None)?;138 let length = subresult.uint32()? as usize;139 if subresult.buf.len() < subresult.offset + length {140 return Err(Error::Error(ExitError::OutOfOffset));141 }142 Ok(subresult.buf[subresult.offset..subresult.offset + length].into())143 }144145 /// Read [`string`] at current position, then advance146 pub fn string(&mut self) -> Result<string> {147 string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))148 }149150 /// Read [`u8`] at current position, then advance151 pub fn uint8(&mut self) -> Result<u8> {152 Ok(self.read_padleft::<1>()?[0])153 }154155 /// Read [`u32`] at current position, then advance156 pub fn uint32(&mut self) -> Result<u32> {157 Ok(u32::from_be_bytes(self.read_padleft()?))158 }159160 /// Read [`u128`] at current position, then advance161 pub fn uint128(&mut self) -> Result<u128> {162 Ok(u128::from_be_bytes(self.read_padleft()?))163 }164165 /// Read [`U256`] at current position, then advance166 pub fn uint256(&mut self) -> Result<U256> {167 let buf: [u8; 32] = self.read_padleft()?;168 Ok(U256::from_big_endian(&buf))169 }170171 /// Read [`u64`] at current position, then advance172 pub fn uint64(&mut self) -> Result<u64> {173 Ok(u64::from_be_bytes(self.read_padleft()?))174 }175176 /// Read [`usize`] at current position, then advance177 #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]178 pub fn read_usize(&mut self) -> Result<usize> {179 Ok(usize::from_be_bytes(self.read_padleft()?))180 }181182 /// Slice recursive buffer, advance one word for buffer offset183 /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`].184 fn subresult(&mut self, size: Option<usize>) -> Result<AbiReader<'i>> {185 let subresult_offset = self.subresult_offset;186 let offset = if let Some(size) = size {187 self.offset += size;188 self.subresult_offset += size;189 0190 } else {191 self.uint32()? as usize192 };193194 if offset + self.subresult_offset > self.buf.len() {195 return Err(Error::Error(ExitError::InvalidRange));196 }197198 let new_offset = offset + subresult_offset;199 Ok(AbiReader {200 buf: self.buf,201 subresult_offset: new_offset,202 offset: new_offset,203 })204 }205206 /// Is this parser reached end of buffer?207 pub fn is_finished(&self) -> bool {208 self.buf.len() == self.offset209 }210}211212/// Writer for RLP encoded data213#[derive(Default)]214pub struct AbiWriter {215 static_part: Vec<u8>,216 dynamic_part: Vec<(usize, AbiWriter)>,217 had_call: bool,218}219impl AbiWriter {220 /// Initialize internal buffers for output data, assuming no padding required221 pub fn new() -> Self {222 Self::default()223 }224 /// Initialize internal buffers, inserting method selector at beginning225 pub fn new_call(method_id: u32) -> Self {226 let mut val = Self::new();227 val.static_part.extend(&method_id.to_be_bytes());228 val.had_call = true;229 val230 }231232 fn write_padleft(&mut self, block: &[u8]) {233 assert!(block.len() <= ABI_ALIGNMENT);234 self.static_part235 .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);236 self.static_part.extend(block);237 }238239 fn write_padright(&mut self, bytes: &[u8]) {240 assert!(bytes.len() <= ABI_ALIGNMENT);241 self.static_part.extend(bytes);242 self.static_part243 .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);244 }245246 /// Write [`H160`] to end of buffer247 pub fn address(&mut self, address: &H160) {248 self.write_padleft(&address.0)249 }250251 /// Write [`bool`] to end of buffer252 pub fn bool(&mut self, value: &bool) {253 self.write_padleft(&[if *value { 1 } else { 0 }])254 }255256 /// Write [`u8`] to end of buffer257 pub fn uint8(&mut self, value: &u8) {258 self.write_padleft(&[*value])259 }260261 /// Write [`u32`] to end of buffer262 pub fn uint32(&mut self, value: &u32) {263 self.write_padleft(&u32::to_be_bytes(*value))264 }265266 /// Write [`u128`] to end of buffer267 pub fn uint128(&mut self, value: &u128) {268 self.write_padleft(&u128::to_be_bytes(*value))269 }270271 /// Write [`U256`] to end of buffer272 pub fn uint256(&mut self, value: &U256) {273 let mut out = [0; 32];274 value.to_big_endian(&mut out);275 self.write_padleft(&out)276 }277278 /// Write [`usize`] to end of buffer279 #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]280 pub fn write_usize(&mut self, value: &usize) {281 self.write_padleft(&usize::to_be_bytes(*value))282 }283284 /// Append recursive data, writing pending offset at end of buffer285 pub fn write_subresult(&mut self, result: Self) {286 self.dynamic_part.push((self.static_part.len(), result));287 // Empty block, to be filled later288 self.write_padleft(&[]);289 }290291 fn memory(&mut self, value: &[u8]) {292 let mut sub = Self::new();293 sub.uint32(&(value.len() as u32));294 for chunk in value.chunks(ABI_ALIGNMENT) {295 sub.write_padright(chunk);296 }297 self.write_subresult(sub);298 }299300 /// Append recursive [`str`] at end of buffer301 pub fn string(&mut self, value: &str) {302 self.memory(value.as_bytes())303 }304305 /// Append recursive [`[u8]`] at end of buffer306 pub fn bytes(&mut self, value: &[u8]) {307 self.memory(value)308 }309310 /// Finish writer, concatenating all internal buffers311 pub fn finish(mut self) -> Vec<u8> {312 for (static_offset, part) in self.dynamic_part {313 let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);314315 let encoded_dynamic_offset = usize::to_be_bytes(part_offset);316 self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()317 ..static_offset + ABI_ALIGNMENT]318 .copy_from_slice(&encoded_dynamic_offset);319 self.static_part.extend(part.finish())320 }321 self.static_part322 }323}324325/// [`AbiReader`] implements reading of many types, but it should326/// be limited to types defined in spec327///328/// As this trait can't be made sealed,329/// instead of having `impl AbiRead for T`, we have `impl AbiRead<T> for AbiReader`330pub trait AbiRead<T> {331 /// Read item from current position, advanding decoder332 fn abi_read(&mut self) -> Result<T>;333 fn size() -> usize;334}335336macro_rules! impl_abi_readable {337 ($ty:ty, $method:ident) => {338 impl AbiRead<$ty> for AbiReader<'_> {339 fn abi_read(&mut self) -> Result<$ty> {340 self.$method()341 }342343 fn size() -> usize {344 ABI_ALIGNMENT345 }346 }347 };348}349350impl_abi_readable!(u8, uint8);351impl_abi_readable!(u32, uint32);352impl_abi_readable!(u64, uint64);353impl_abi_readable!(u128, uint128);354impl_abi_readable!(U256, uint256);355impl_abi_readable!([u8; 4], bytes4);356impl_abi_readable!(H160, address);357impl_abi_readable!(Vec<u8>, bytes);358impl_abi_readable!(bool, bool);359impl_abi_readable!(string, string);360361mod sealed {362 /// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead363 pub trait CanBePlacedInVec {}364}365366impl sealed::CanBePlacedInVec for U256 {}367impl sealed::CanBePlacedInVec for string {}368impl sealed::CanBePlacedInVec for H160 {}369370impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>371where372 Self: AbiRead<R>,373{374 fn abi_read(&mut self) -> Result<Vec<R>> {375 let mut sub = self.subresult(None)?;376 let size = sub.uint32()? as usize;377 sub.subresult_offset = sub.offset;378 let mut out = Vec::with_capacity(size);379 for _ in 0..size {380 out.push(<Self as AbiRead<R>>::abi_read(&mut sub)?);381 }382 Ok(out)383 }384385 fn size() -> usize {386 ABI_ALIGNMENT387 }388}389390macro_rules! impl_tuples {391 ($($ident:ident)+) => {392 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}393 impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>394 where395 $(396 Self: AbiRead<$ident>,397 )+398 ($($ident,)+): SolidityTypeName,399 {400 fn abi_read(&mut self) -> Result<($($ident,)+)> {401 let size = if <($($ident,)+)>::is_simple() { Some(<Self as AbiRead<($($ident,)+)>>::size()) } else { None };402 let mut subresult = self.subresult(size)?;403 Ok((404 $(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+405 ))406 }407408 fn size() -> usize {409 0 $(+ {let _ : $ident; ABI_ALIGNMENT})+410 }411 }412 #[allow(non_snake_case)]413 impl<$($ident),+> AbiWrite for &($($ident,)+)414 where415 $($ident: AbiWrite,)+416 {417 fn abi_write(&self, writer: &mut AbiWriter) {418 let ($($ident,)+) = self;419 $($ident.abi_write(writer);)+420 }421 }422 };423}424425impl_tuples! {A}426impl_tuples! {A B}427impl_tuples! {A B C}428impl_tuples! {A B C D}429impl_tuples! {A B C D E}430impl_tuples! {A B C D E F}431impl_tuples! {A B C D E F G}432impl_tuples! {A B C D E F G H}433impl_tuples! {A B C D E F G H I}434impl_tuples! {A B C D E F G H I J}435436/// For questions about inability to provide custom implementations,437/// see [`AbiRead`]438pub trait AbiWrite {439 /// Write value to end of specified encoder440 fn abi_write(&self, writer: &mut AbiWriter);441 /// Specialization for [`crate::solidity_interface`] implementation,442 /// see comment in `impl AbiWrite for ResultWithPostInfo`443 fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {444 let mut writer = AbiWriter::new();445 self.abi_write(&mut writer);446 Ok(writer.into())447 }448}449450/// This particular AbiWrite implementation should be split to another trait,451/// which only implements `to_result`, but due to lack of specialization feature452/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,453/// so here we abusing default trait methods for it454impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {455 fn abi_write(&self, _writer: &mut AbiWriter) {456 debug_assert!(false, "shouldn't be called, see comment")457 }458 fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {459 match self {460 Ok(v) => Ok(WithPostDispatchInfo {461 post_info: v.post_info.clone(),462 data: {463 let mut out = AbiWriter::new();464 v.data.abi_write(&mut out);465 out466 },467 }),468 Err(e) => Err(e.clone()),469 }470 }471}472473macro_rules! impl_abi_writeable {474 ($ty:ty, $method:ident) => {475 impl AbiWrite for $ty {476 fn abi_write(&self, writer: &mut AbiWriter) {477 writer.$method(&self)478 }479 }480 };481}482483impl_abi_writeable!(u8, uint8);484impl_abi_writeable!(u32, uint32);485impl_abi_writeable!(u128, uint128);486impl_abi_writeable!(U256, uint256);487impl_abi_writeable!(H160, address);488impl_abi_writeable!(bool, bool);489impl_abi_writeable!(&str, string);490impl AbiWrite for &string {491 fn abi_write(&self, writer: &mut AbiWriter) {492 writer.string(self)493 }494}495impl AbiWrite for &Vec<u8> {496 fn abi_write(&self, writer: &mut AbiWriter) {497 writer.bytes(self)498 }499}500501impl AbiWrite for () {502 fn abi_write(&self, _writer: &mut AbiWriter) {}503}504505/// Helper macros to parse reader into variables506#[deprecated]507#[macro_export]508macro_rules! abi_decode {509 ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {510 $(511 let $name = $reader.$typ()?;512 )+513 }514}515516/// Helper macros to construct RLP-encoded buffer517#[deprecated]518#[macro_export]519macro_rules! abi_encode {520 ($($typ:ident($value:expr)),* $(,)?) => {{521 #[allow(unused_mut)]522 let mut writer = ::evm_coder::abi::AbiWriter::new();523 $(524 writer.$typ($value);525 )*526 writer527 }};528 (call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{529 #[allow(unused_mut)]530 let mut writer = ::evm_coder::abi::AbiWriter::new_call($val);531 $(532 writer.$typ($value);533 )*534 writer535 }}536}537538#[cfg(test)]539pub mod test {540 use crate::{541 abi::AbiRead,542 types::{string, uint256},543 };544545 use super::{AbiReader, AbiWriter};546 use hex_literal::hex;547548 #[test]549 fn dynamic_after_static() {550 let mut encoder = AbiWriter::new();551 encoder.bool(&true);552 encoder.string("test");553 let encoded = encoder.finish();554555 let mut encoder = AbiWriter::new();556 encoder.bool(&true);557 // Offset to subresult558 encoder.uint32(&(32 * 2));559 // Len of "test"560 encoder.uint32(&4);561 encoder.write_padright(&[b't', b'e', b's', b't']);562 let alternative_encoded = encoder.finish();563564 assert_eq!(encoded, alternative_encoded);565566 let mut decoder = AbiReader::new(&encoded);567 assert!(decoder.bool().unwrap());568 assert_eq!(decoder.string().unwrap(), "test");569 }570571 #[test]572 fn mint_sample() {573 let (call, mut decoder) = AbiReader::new_call(&hex!(574 "575 50bb4e7f576 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374577 0000000000000000000000000000000000000000000000000000000000000001578 0000000000000000000000000000000000000000000000000000000000000060579 0000000000000000000000000000000000000000000000000000000000000008580 5465737420555249000000000000000000000000000000000000000000000000581 "582 ))583 .unwrap();584 assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));585 assert_eq!(586 format!("{:?}", decoder.address().unwrap()),587 "0xad2c0954693c2b5404b7e50967d3481bea432374"588 );589 assert_eq!(decoder.uint32().unwrap(), 1);590 assert_eq!(decoder.string().unwrap(), "Test URI");591 }592593 #[test]594 fn mint_bulk() {595 let (call, mut decoder) = AbiReader::new_call(&hex!(596 "597 36543006598 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address599 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]600 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]601602 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem603 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem604 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem605606 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60607 0000000000000000000000000000000000000000000000000000000000000040 // offset of string608 000000000000000000000000000000000000000000000000000000000000000a // size of string609 5465737420555249203000000000000000000000000000000000000000000000 // string610611 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0612 0000000000000000000000000000000000000000000000000000000000000040 // offset of string613 000000000000000000000000000000000000000000000000000000000000000a // size of string614 5465737420555249203100000000000000000000000000000000000000000000 // string615616 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160617 0000000000000000000000000000000000000000000000000000000000000040 // offset of string618 000000000000000000000000000000000000000000000000000000000000000a // size of string619 5465737420555249203200000000000000000000000000000000000000000000 // string620 "621 ))622 .unwrap();623 assert_eq!(call, u32::to_be_bytes(0x36543006));624 let _ = decoder.address().unwrap();625 let data =626 <AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();627 assert_eq!(628 data,629 vec![630 (1.into(), "Test URI 0".to_string()),631 (11.into(), "Test URI 1".to_string()),632 (12.into(), "Test URI 2".to_string())633 ]634 );635 }636637 #[test]638 fn parse_vec_with_simple_type() {639 use crate::types::address;640 use primitive_types::{H160, U256};641642 let (call, mut decoder) = AbiReader::new_call(&hex!(643 "644 1ACF2D55645 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]646 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]647648 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address649 000000000000000000000000000000000000000000000000000000000000000A // uint256650651 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address652 0000000000000000000000000000000000000000000000000000000000000014 // uint256653654 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address655 000000000000000000000000000000000000000000000000000000000000001E // uint256656 "657 ))658 .unwrap();659 assert_eq!(call, u32::to_be_bytes(0x1ACF2D55));660 let data =661 <AbiReader<'_> as AbiRead<Vec<(address, uint256)>>>::abi_read(&mut decoder).unwrap();662 assert_eq!(data.len(), 3);663 assert_eq!(664 data,665 vec![666 (667 H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")),668 U256([10, 0, 0, 0])669 ),670 (671 H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")),672 U256([20, 0, 0, 0])673 ),674 (675 H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")),676 U256([30, 0, 0, 0])677 ),678 ]679 );680 }681}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/>.1617//! Implementation of EVM RLP reader/writer1819#![allow(dead_code)]2021#[cfg(not(feature = "std"))]22use alloc::vec::Vec;23use evm_core::ExitError;24use primitive_types::{H160, U256};2526use crate::{27 execution::{Error, ResultWithPostInfo, WithPostDispatchInfo},28 types::{string, self},29};30use crate::execution::Result;3132const ABI_ALIGNMENT: usize = 32;3334trait TypeHelper<T> {35 fn is_dynamic() -> bool;36}3738/// View into RLP data, which provides method to read typed items from it39#[derive(Clone)]40pub struct AbiReader<'i> {41 buf: &'i [u8],42 subresult_offset: usize,43 offset: usize,44}45impl<'i> AbiReader<'i> {46 /// Start reading RLP buffer, assuming there is no padding bytes47 pub fn new(buf: &'i [u8]) -> Self {48 Self {49 buf,50 subresult_offset: 0,51 offset: 0,52 }53 }54 /// Start reading RLP buffer, parsing first 4 bytes as selector55 pub fn new_call(buf: &'i [u8]) -> Result<(types::bytes4, Self)> {56 if buf.len() < 4 {57 return Err(Error::Error(ExitError::OutOfOffset));58 }59 let mut method_id = [0; 4];60 method_id.copy_from_slice(&buf[0..4]);6162 Ok((63 method_id,64 Self {65 buf,66 subresult_offset: 4,67 offset: 4,68 },69 ))70 }7172 fn read_pad<const S: usize>(73 buf: &[u8],74 offset: usize,75 pad_start: usize,76 pad_size: usize,77 block_start: usize,78 block_size: usize,79 ) -> Result<[u8; S]> {80 if buf.len() - offset < ABI_ALIGNMENT {81 return Err(Error::Error(ExitError::OutOfOffset));82 }83 let mut block = [0; S];84 let is_pad_zeroed = buf[pad_start..pad_size].iter().all(|&v| v == 0);85 if !is_pad_zeroed {86 return Err(Error::Error(ExitError::InvalidRange));87 }88 block.copy_from_slice(&buf[block_start..block_size]);89 Ok(block)90 }9192 fn read_padleft<const S: usize>(&mut self) -> Result<[u8; S]> {93 let offset = self.offset;94 self.offset += ABI_ALIGNMENT;95 Self::read_pad(96 self.buf,97 offset,98 offset,99 offset + ABI_ALIGNMENT - S,100 offset + ABI_ALIGNMENT - S,101 offset + ABI_ALIGNMENT,102 )103 }104105 fn read_padright<const S: usize>(&mut self) -> Result<[u8; S]> {106 let offset = self.offset;107 self.offset += ABI_ALIGNMENT;108 Self::read_pad(109 self.buf,110 offset,111 offset + S,112 offset + ABI_ALIGNMENT,113 offset,114 offset + S,115 )116 }117118 /// Read [`H160`] at current position, then advance119 pub fn address(&mut self) -> Result<H160> {120 Ok(H160(self.read_padleft()?))121 }122123 /// Read [`bool`] at current position, then advance124 pub fn bool(&mut self) -> Result<bool> {125 let data: [u8; 1] = self.read_padleft()?;126 match data[0] {127 0 => Ok(false),128 1 => Ok(true),129 _ => Err(Error::Error(ExitError::InvalidRange)),130 }131 }132133 /// Read [`[u8; 4]`] at current position, then advance134 pub fn bytes4(&mut self) -> Result<[u8; 4]> {135 self.read_padright()136 }137138 /// Read [`Vec<u8>`] at current position, then advance139 pub fn bytes(&mut self) -> Result<Vec<u8>> {140 let mut subresult = self.subresult(None)?;141 let length = subresult.uint32()? as usize;142 if subresult.buf.len() < subresult.offset + length {143 return Err(Error::Error(ExitError::OutOfOffset));144 }145 Ok(subresult.buf[subresult.offset..subresult.offset + length].into())146 }147148 /// Read [`string`] at current position, then advance149 pub fn string(&mut self) -> Result<string> {150 string::from_utf8(self.bytes()?).map_err(|_| Error::Error(ExitError::InvalidRange))151 }152153 /// Read [`u8`] at current position, then advance154 pub fn uint8(&mut self) -> Result<u8> {155 Ok(self.read_padleft::<1>()?[0])156 }157158 /// Read [`u32`] at current position, then advance159 pub fn uint32(&mut self) -> Result<u32> {160 Ok(u32::from_be_bytes(self.read_padleft()?))161 }162163 /// Read [`u128`] at current position, then advance164 pub fn uint128(&mut self) -> Result<u128> {165 Ok(u128::from_be_bytes(self.read_padleft()?))166 }167168 /// Read [`U256`] at current position, then advance169 pub fn uint256(&mut self) -> Result<U256> {170 let buf: [u8; 32] = self.read_padleft()?;171 Ok(U256::from_big_endian(&buf))172 }173174 /// Read [`u64`] at current position, then advance175 pub fn uint64(&mut self) -> Result<u64> {176 Ok(u64::from_be_bytes(self.read_padleft()?))177 }178179 /// Read [`usize`] at current position, then advance180 #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]181 pub fn read_usize(&mut self) -> Result<usize> {182 Ok(usize::from_be_bytes(self.read_padleft()?))183 }184185 /// Slice recursive buffer, advance one word for buffer offset186 /// If `size` is [`None`] then [`Self::offset`] and [`Self::subresult_offset`] evals from [`Self::buf`].187 fn subresult(&mut self, size: Option<usize>) -> Result<AbiReader<'i>> {188 let subresult_offset = self.subresult_offset;189 let offset = if let Some(size) = size {190 self.offset += size;191 self.subresult_offset += size;192 0193 } else {194 self.uint32()? as usize195 };196197 if offset + self.subresult_offset > self.buf.len() {198 return Err(Error::Error(ExitError::InvalidRange));199 }200201 let new_offset = offset + subresult_offset;202 Ok(AbiReader {203 buf: self.buf,204 subresult_offset: new_offset,205 offset: new_offset,206 })207 }208209 /// Is this parser reached end of buffer?210 pub fn is_finished(&self) -> bool {211 self.buf.len() == self.offset212 }213}214215/// Writer for RLP encoded data216#[derive(Default)]217pub struct AbiWriter {218 static_part: Vec<u8>,219 dynamic_part: Vec<(usize, AbiWriter)>,220 had_call: bool,221}222impl AbiWriter {223 /// Initialize internal buffers for output data, assuming no padding required224 pub fn new() -> Self {225 Self::default()226 }227 /// Initialize internal buffers, inserting method selector at beginning228 pub fn new_call(method_id: u32) -> Self {229 let mut val = Self::new();230 val.static_part.extend(&method_id.to_be_bytes());231 val.had_call = true;232 val233 }234235 fn write_padleft(&mut self, block: &[u8]) {236 assert!(block.len() <= ABI_ALIGNMENT);237 self.static_part238 .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - block.len()]);239 self.static_part.extend(block);240 }241242 fn write_padright(&mut self, bytes: &[u8]) {243 assert!(bytes.len() <= ABI_ALIGNMENT);244 self.static_part.extend(bytes);245 self.static_part246 .extend(&[0; ABI_ALIGNMENT][0..ABI_ALIGNMENT - bytes.len()]);247 }248249 /// Write [`H160`] to end of buffer250 pub fn address(&mut self, address: &H160) {251 self.write_padleft(&address.0)252 }253254 /// Write [`bool`] to end of buffer255 pub fn bool(&mut self, value: &bool) {256 self.write_padleft(&[if *value { 1 } else { 0 }])257 }258259 /// Write [`u8`] to end of buffer260 pub fn uint8(&mut self, value: &u8) {261 self.write_padleft(&[*value])262 }263264 /// Write [`u32`] to end of buffer265 pub fn uint32(&mut self, value: &u32) {266 self.write_padleft(&u32::to_be_bytes(*value))267 }268269 /// Write [`u128`] to end of buffer270 pub fn uint128(&mut self, value: &u128) {271 self.write_padleft(&u128::to_be_bytes(*value))272 }273274 /// Write [`U256`] to end of buffer275 pub fn uint256(&mut self, value: &U256) {276 let mut out = [0; 32];277 value.to_big_endian(&mut out);278 self.write_padleft(&out)279 }280281 /// Write [`usize`] to end of buffer282 #[deprecated = "dangerous, as usize may have different width in wasm and native execution"]283 pub fn write_usize(&mut self, value: &usize) {284 self.write_padleft(&usize::to_be_bytes(*value))285 }286287 /// Append recursive data, writing pending offset at end of buffer288 pub fn write_subresult(&mut self, result: Self) {289 self.dynamic_part.push((self.static_part.len(), result));290 // Empty block, to be filled later291 self.write_padleft(&[]);292 }293294 fn memory(&mut self, value: &[u8]) {295 let mut sub = Self::new();296 sub.uint32(&(value.len() as u32));297 for chunk in value.chunks(ABI_ALIGNMENT) {298 sub.write_padright(chunk);299 }300 self.write_subresult(sub);301 }302303 /// Append recursive [`str`] at end of buffer304 pub fn string(&mut self, value: &str) {305 self.memory(value.as_bytes())306 }307308 /// Append recursive [`[u8]`] at end of buffer309 pub fn bytes(&mut self, value: &[u8]) {310 self.memory(value)311 }312313 /// Finish writer, concatenating all internal buffers314 pub fn finish(mut self) -> Vec<u8> {315 for (static_offset, part) in self.dynamic_part {316 let part_offset = self.static_part.len() - self.had_call.then(|| 4).unwrap_or(0);317318 let encoded_dynamic_offset = usize::to_be_bytes(part_offset);319 self.static_part[static_offset + ABI_ALIGNMENT - encoded_dynamic_offset.len()320 ..static_offset + ABI_ALIGNMENT]321 .copy_from_slice(&encoded_dynamic_offset);322 self.static_part.extend(part.finish())323 }324 self.static_part325 }326}327328/// [`AbiReader`] implements reading of many types, but it should329/// be limited to types defined in spec330///331/// As this trait can't be made sealed,332/// instead of having `impl AbiRead for T`, we have `impl AbiRead<T> for AbiReader`333pub trait AbiRead<T> {334 /// Read item from current position, advanding decoder335 fn abi_read(&mut self) -> Result<T>;336337 /// Size for type aligned to [`ABI_ALIGNMENT`].338 fn size() -> usize;339}340341macro_rules! impl_abi_readable {342 ($ty:ty, $method:ident, $dynamic:literal) => {343 impl TypeHelper<$ty> for $ty {344 fn is_dynamic() -> bool {345 $dynamic346 }347 }348 impl AbiRead<$ty> for AbiReader<'_> {349 fn abi_read(&mut self) -> Result<$ty> {350 self.$method()351 }352353 fn size() -> usize {354 ABI_ALIGNMENT355 }356 }357 };358}359360impl_abi_readable!(u8, uint8, false);361impl_abi_readable!(u32, uint32, false);362impl_abi_readable!(u64, uint64, false);363impl_abi_readable!(u128, uint128, false);364impl_abi_readable!(U256, uint256, false);365impl_abi_readable!([u8; 4], bytes4, false);366impl_abi_readable!(H160, address, false);367impl_abi_readable!(Vec<u8>, bytes, true);368impl_abi_readable!(bool, bool, true);369impl_abi_readable!(string, string, true);370371mod sealed {372 /// Not all types can be placed in vec, i.e `Vec<u8>` is restricted, `bytes` should be used instead373 pub trait CanBePlacedInVec {}374}375376impl sealed::CanBePlacedInVec for U256 {}377impl sealed::CanBePlacedInVec for string {}378impl sealed::CanBePlacedInVec for H160 {}379380impl<R: sealed::CanBePlacedInVec> AbiRead<Vec<R>> for AbiReader<'_>381where382 Self: AbiRead<R>,383{384 fn abi_read(&mut self) -> Result<Vec<R>> {385 let mut sub = self.subresult(None)?;386 let size = sub.uint32()? as usize;387 sub.subresult_offset = sub.offset;388 let mut out = Vec::with_capacity(size);389 for _ in 0..size {390 out.push(<Self as AbiRead<R>>::abi_read(&mut sub)?);391 }392 Ok(out)393 }394395 fn size() -> usize {396 ABI_ALIGNMENT397 }398}399400macro_rules! impl_tuples {401 ($($ident:ident)+) => {402 impl<$($ident: TypeHelper<$ident>,)+> TypeHelper<($($ident,)+)> for ($($ident,)+) {403 fn is_dynamic() -> bool {404 false405 $(406 || <$ident>::is_dynamic()407 )*408 }409 }410 impl<$($ident),+> sealed::CanBePlacedInVec for ($($ident,)+) {}411 impl<$($ident),+> AbiRead<($($ident,)+)> for AbiReader<'_>412 where413 $(414 Self: AbiRead<$ident>,415 )+416 ($($ident,)+): TypeHelper<($($ident,)+)>,417 {418 fn abi_read(&mut self) -> Result<($($ident,)+)> {419 let size = if !<($($ident,)+)>::is_dynamic() { Some(<Self as AbiRead<($($ident,)+)>>::size()) } else { None };420 let mut subresult = self.subresult(size)?;421 Ok((422 $(<Self as AbiRead<$ident>>::abi_read(&mut subresult)?,)+423 ))424 }425426 fn size() -> usize {427 0 $(+ {let _ : $ident; ABI_ALIGNMENT})+428 }429 }430 #[allow(non_snake_case)]431 impl<$($ident),+> AbiWrite for &($($ident,)+)432 where433 $($ident: AbiWrite,)+434 {435 fn abi_write(&self, writer: &mut AbiWriter) {436 let ($($ident,)+) = self;437 $($ident.abi_write(writer);)+438 }439 }440 };441}442443impl_tuples! {A}444impl_tuples! {A B}445impl_tuples! {A B C}446impl_tuples! {A B C D}447impl_tuples! {A B C D E}448impl_tuples! {A B C D E F}449impl_tuples! {A B C D E F G}450impl_tuples! {A B C D E F G H}451impl_tuples! {A B C D E F G H I}452impl_tuples! {A B C D E F G H I J}453454/// For questions about inability to provide custom implementations,455/// see [`AbiRead`]456pub trait AbiWrite {457 /// Write value to end of specified encoder458 fn abi_write(&self, writer: &mut AbiWriter);459 /// Specialization for [`crate::solidity_interface`] implementation,460 /// see comment in `impl AbiWrite for ResultWithPostInfo`461 fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {462 let mut writer = AbiWriter::new();463 self.abi_write(&mut writer);464 Ok(writer.into())465 }466}467468/// This particular AbiWrite implementation should be split to another trait,469/// which only implements `to_result`, but due to lack of specialization feature470/// in stable Rust, we can't have blanket impl of this trait `for T where T: AbiWrite`,471/// so here we abusing default trait methods for it472impl<T: AbiWrite> AbiWrite for ResultWithPostInfo<T> {473 fn abi_write(&self, _writer: &mut AbiWriter) {474 debug_assert!(false, "shouldn't be called, see comment")475 }476 fn to_result(&self) -> ResultWithPostInfo<AbiWriter> {477 match self {478 Ok(v) => Ok(WithPostDispatchInfo {479 post_info: v.post_info.clone(),480 data: {481 let mut out = AbiWriter::new();482 v.data.abi_write(&mut out);483 out484 },485 }),486 Err(e) => Err(e.clone()),487 }488 }489}490491macro_rules! impl_abi_writeable {492 ($ty:ty, $method:ident) => {493 impl AbiWrite for $ty {494 fn abi_write(&self, writer: &mut AbiWriter) {495 writer.$method(&self)496 }497 }498 };499}500501impl_abi_writeable!(u8, uint8);502impl_abi_writeable!(u32, uint32);503impl_abi_writeable!(u128, uint128);504impl_abi_writeable!(U256, uint256);505impl_abi_writeable!(H160, address);506impl_abi_writeable!(bool, bool);507impl_abi_writeable!(&str, string);508impl AbiWrite for &string {509 fn abi_write(&self, writer: &mut AbiWriter) {510 writer.string(self)511 }512}513impl AbiWrite for &Vec<u8> {514 fn abi_write(&self, writer: &mut AbiWriter) {515 writer.bytes(self)516 }517}518519impl AbiWrite for () {520 fn abi_write(&self, _writer: &mut AbiWriter) {}521}522523/// Helper macros to parse reader into variables524#[deprecated]525#[macro_export]526macro_rules! abi_decode {527 ($reader:expr, $($name:ident: $typ:ident),+ $(,)?) => {528 $(529 let $name = $reader.$typ()?;530 )+531 }532}533534/// Helper macros to construct RLP-encoded buffer535#[deprecated]536#[macro_export]537macro_rules! abi_encode {538 ($($typ:ident($value:expr)),* $(,)?) => {{539 #[allow(unused_mut)]540 let mut writer = ::evm_coder::abi::AbiWriter::new();541 $(542 writer.$typ($value);543 )*544 writer545 }};546 (call $val:expr; $($typ:ident($value:expr)),* $(,)?) => {{547 #[allow(unused_mut)]548 let mut writer = ::evm_coder::abi::AbiWriter::new_call($val);549 $(550 writer.$typ($value);551 )*552 writer553 }}554}555556#[cfg(test)]557pub mod test {558 use crate::{559 abi::AbiRead,560 types::{string, uint256},561 };562563 use super::{AbiReader, AbiWriter};564 use hex_literal::hex;565566 #[test]567 fn dynamic_after_static() {568 let mut encoder = AbiWriter::new();569 encoder.bool(&true);570 encoder.string("test");571 let encoded = encoder.finish();572573 let mut encoder = AbiWriter::new();574 encoder.bool(&true);575 // Offset to subresult576 encoder.uint32(&(32 * 2));577 // Len of "test"578 encoder.uint32(&4);579 encoder.write_padright(&[b't', b'e', b's', b't']);580 let alternative_encoded = encoder.finish();581582 assert_eq!(encoded, alternative_encoded);583584 let mut decoder = AbiReader::new(&encoded);585 assert!(decoder.bool().unwrap());586 assert_eq!(decoder.string().unwrap(), "test");587 }588589 #[test]590 fn mint_sample() {591 let (call, mut decoder) = AbiReader::new_call(&hex!(592 "593 50bb4e7f594 000000000000000000000000ad2c0954693c2b5404b7e50967d3481bea432374595 0000000000000000000000000000000000000000000000000000000000000001596 0000000000000000000000000000000000000000000000000000000000000060597 0000000000000000000000000000000000000000000000000000000000000008598 5465737420555249000000000000000000000000000000000000000000000000599 "600 ))601 .unwrap();602 assert_eq!(call, u32::to_be_bytes(0x50bb4e7f));603 assert_eq!(604 format!("{:?}", decoder.address().unwrap()),605 "0xad2c0954693c2b5404b7e50967d3481bea432374"606 );607 assert_eq!(decoder.uint32().unwrap(), 1);608 assert_eq!(decoder.string().unwrap(), "Test URI");609 }610611 #[test]612 fn mint_bulk() {613 let (call, mut decoder) = AbiReader::new_call(&hex!(614 "615 36543006616 00000000000000000000000053744e6da587ba10b32a2554d2efdcd985bc27a3 // address617 0000000000000000000000000000000000000000000000000000000000000040 // offset of (uint256, string)[]618 0000000000000000000000000000000000000000000000000000000000000003 // length of (uint256, string)[]619620 0000000000000000000000000000000000000000000000000000000000000060 // offset of first elem621 00000000000000000000000000000000000000000000000000000000000000e0 // offset of second elem622 0000000000000000000000000000000000000000000000000000000000000160 // offset of third elem623624 0000000000000000000000000000000000000000000000000000000000000001 // first token id? #60625 0000000000000000000000000000000000000000000000000000000000000040 // offset of string626 000000000000000000000000000000000000000000000000000000000000000a // size of string627 5465737420555249203000000000000000000000000000000000000000000000 // string628629 000000000000000000000000000000000000000000000000000000000000000b // second token id? Why ==11? #e0630 0000000000000000000000000000000000000000000000000000000000000040 // offset of string631 000000000000000000000000000000000000000000000000000000000000000a // size of string632 5465737420555249203100000000000000000000000000000000000000000000 // string633634 000000000000000000000000000000000000000000000000000000000000000c // third token id? Why ==12? #160635 0000000000000000000000000000000000000000000000000000000000000040 // offset of string636 000000000000000000000000000000000000000000000000000000000000000a // size of string637 5465737420555249203200000000000000000000000000000000000000000000 // string638 "639 ))640 .unwrap();641 assert_eq!(call, u32::to_be_bytes(0x36543006));642 let _ = decoder.address().unwrap();643 let data =644 <AbiReader<'_> as AbiRead<Vec<(uint256, string)>>>::abi_read(&mut decoder).unwrap();645 assert_eq!(646 data,647 vec![648 (1.into(), "Test URI 0".to_string()),649 (11.into(), "Test URI 1".to_string()),650 (12.into(), "Test URI 2".to_string())651 ]652 );653 }654655 #[test]656 fn parse_vec_with_simple_type() {657 use crate::types::address;658 use primitive_types::{H160, U256};659660 let (call, mut decoder) = AbiReader::new_call(&hex!(661 "662 1ACF2D55663 0000000000000000000000000000000000000000000000000000000000000020 // offset of (address, uint256)[]664 0000000000000000000000000000000000000000000000000000000000000003 // length of (address, uint256)[]665666 0000000000000000000000002D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC // address667 000000000000000000000000000000000000000000000000000000000000000A // uint256668669 000000000000000000000000AB8E3D9134955566483B11E6825C9223B6737B10 // address670 0000000000000000000000000000000000000000000000000000000000000014 // uint256671672 0000000000000000000000008C582BDF2953046705FC56F189385255EFC1BE18 // address673 000000000000000000000000000000000000000000000000000000000000001E // uint256674 "675 ))676 .unwrap();677 assert_eq!(call, u32::to_be_bytes(0x1ACF2D55));678 let data =679 <AbiReader<'_> as AbiRead<Vec<(address, uint256)>>>::abi_read(&mut decoder).unwrap();680 assert_eq!(data.len(), 3);681 assert_eq!(682 data,683 vec![684 (685 H160(hex!("2D2FF76104B7BACB2E8F6731D5BFC184EBECDDBC")),686 U256([10, 0, 0, 0])687 ),688 (689 H160(hex!("AB8E3D9134955566483B11E6825C9223B6737B10")),690 U256([20, 0, 0, 0])691 ),692 (693 H160(hex!("8C582BDF2953046705FC56F189385255EFC1BE18")),694 U256([30, 0, 0, 0])695 ),696 ]697 );698 }699}crates/evm-coder/src/solidity.rsdiffbeforeafterboth--- a/crates/evm-coder/src/solidity.rs
+++ b/crates/evm-coder/src/solidity.rs
@@ -192,10 +192,7 @@
write!(writer, "{}", tc.collect_tuple::<Self>())
}
fn is_simple() -> bool {
- true
- $(
- && <$ident>::is_simple()
- )*
+ false
}
#[allow(unused_assignments)]
fn solidity_default(writer: &mut impl fmt::Write, tc: &TypeCollector) -> fmt::Result {
pallets/fungible/src/erc.rsdiffbeforeafterboth--- a/pallets/fungible/src/erc.rs
+++ b/pallets/fungible/src/erc.rs
@@ -129,7 +129,7 @@
}
}
-#[solidity_interface(name = "ERC20Mintable")]
+#[solidity_interface(name = ERC20Mintable)]
impl<T: Config> FungibleHandle<T> {
/// Mint tokens for `to` account.
/// @param to account that will receive minted tokens
@@ -148,7 +148,7 @@
}
}
-#[solidity_interface(name = "ERC20UniqueExtensions")]
+#[solidity_interface(name = ERC20UniqueExtensions)]
impl<T: Config> FungibleHandle<T> {
/// Burn tokens from account
/// @dev Function that burns an `amount` of the tokens of a given account,
pallets/fungible/src/stubs/UniqueFungible.rawdiffbeforeafterbothbinary blob — no preview
pallets/fungible/src/stubs/UniqueFungible.soldiffbeforeafterboth--- a/pallets/fungible/src/stubs/UniqueFungible.sol
+++ b/pallets/fungible/src/stubs/UniqueFungible.sol
@@ -3,13 +3,7 @@
pragma solidity >=0.8.0 <0.9.0;
-// Anonymous struct
-struct Tuple0 {
- address field_0;
- uint256 field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
contract Dummy {
uint8 dummy;
string stub_error = "this contract is implemented in native";
@@ -27,49 +21,8 @@
}
}
-// Inline
-contract ERC20Events {
- event Transfer(address indexed from, address indexed to, uint256 value);
- event Approval(
- address indexed owner,
- address indexed spender,
- uint256 value
- );
-}
-
-// Selector: 40c10f19
-contract ERC20Mintable is Dummy, ERC165 {
- // Selector: mint(address,uint256) 40c10f19
- function mint(address to, uint256 amount) public returns (bool) {
- require(false, stub_error);
- to;
- amount;
- dummy = 0;
- return false;
- }
-}
-
-// Selector: 63034ac5
-contract ERC20UniqueExtensions is Dummy, ERC165 {
- // Selector: burnFrom(address,uint256) 79cc6790
- function burnFrom(address from, uint256 amount) public returns (bool) {
- require(false, stub_error);
- from;
- amount;
- dummy = 0;
- return false;
- }
-
- // Selector: mintBulk((address,uint256)[]) 1acf2d55
- function mintBulk(Tuple0[] memory amounts) public returns (bool) {
- require(false, stub_error);
- amounts;
- dummy = 0;
- return false;
- }
-}
-
-// Selector: 6cf113cd
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xe54be640
contract Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -397,19 +350,51 @@
}
}
+/// @dev the ERC-165 identifier for this interface is 0x63034ac5
+contract ERC20UniqueExtensions is Dummy, ERC165 {
+ /// Burn tokens from account
+ /// @dev Function that burns an `amount` of the tokens of a given account,
+ /// deducting from the sender's allowance for said account.
+ /// @param from The account whose tokens will be burnt.
+ /// @param amount The amount that will be burnt.
+ /// @dev EVM selector for this function is: 0x79cc6790,
+ /// or in textual repr: burnFrom(address,uint256)
+ function burnFrom(address from, uint256 amount) public returns (bool) {
+ require(false, stub_error);
+ from;
+ amount;
+ dummy = 0;
+ return false;
+ }
+
+ /// Mint tokens for multiple accounts.
+ /// @param amounts array of pairs of account address and amount
+ /// @dev EVM selector for this function is: 0x1acf2d55,
+ /// or in textual repr: mintBulk((address,uint256)[])
+ function mintBulk(Tuple6[] memory amounts) public returns (bool) {
+ require(false, stub_error);
+ amounts;
+ dummy = 0;
+ return false;
+ }
+}
+
/// @dev anonymous struct
struct Tuple6 {
address field_0;
uint256 field_1;
}
-/// @dev the ERC-165 identifier for this interface is 0x79cc6790
-contract ERC20UniqueExtensions is Dummy, ERC165 {
- /// @dev EVM selector for this function is: 0x79cc6790,
- /// or in textual repr: burnFrom(address,uint256)
- function burnFrom(address from, uint256 amount) public returns (bool) {
+/// @dev the ERC-165 identifier for this interface is 0x40c10f19
+contract ERC20Mintable is Dummy, ERC165 {
+ /// Mint tokens for `to` account.
+ /// @param to account that will receive minted tokens
+ /// @param amount amount of tokens to mint
+ /// @dev EVM selector for this function is: 0x40c10f19,
+ /// or in textual repr: mint(address,uint256)
+ function mint(address to, uint256 amount) public returns (bool) {
require(false, stub_error);
- from;
+ to;
amount;
dummy = 0;
return false;
tests/src/eth/api/UniqueFungible.soldiffbeforeafterboth--- a/tests/src/eth/api/UniqueFungible.sol
+++ b/tests/src/eth/api/UniqueFungible.sol
@@ -3,13 +3,7 @@
pragma solidity >=0.8.0 <0.9.0;
-// Anonymous struct
-struct Tuple0 {
- address field_0;
- uint256 field_1;
-}
-
-// Common stubs holder
+/// @dev common stubs holder
interface Dummy {
}
@@ -18,32 +12,8 @@
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}
-// Inline
-interface ERC20Events {
- event Transfer(address indexed from, address indexed to, uint256 value);
- event Approval(
- address indexed owner,
- address indexed spender,
- uint256 value
- );
-}
-
-// Selector: 40c10f19
-interface ERC20Mintable is Dummy, ERC165 {
- // Selector: mint(address,uint256) 40c10f19
- function mint(address to, uint256 amount) external returns (bool);
-}
-
-// Selector: 63034ac5
-interface ERC20UniqueExtensions is Dummy, ERC165 {
- // Selector: burnFrom(address,uint256) 79cc6790
- function burnFrom(address from, uint256 amount) external returns (bool);
-
- // Selector: mintBulk((address,uint256)[]) 1acf2d55
- function mintBulk(Tuple0[] memory amounts) external returns (bool);
-}
-
-// Selector: 6cf113cd
+/// @title A contract that allows you to work with collections.
+/// @dev the ERC-165 identifier for this interface is 0xe54be640
interface Collection is Dummy, ERC165 {
/// Set collection property.
///
@@ -237,13 +207,56 @@
/// @dev EVM selector for this function is: 0xd34b55b8,
/// or in textual repr: uniqueCollectionType()
function uniqueCollectionType() external returns (string memory);
+
+ /// Changes collection owner to another account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner account
+ /// @dev EVM selector for this function is: 0x13af4035,
+ /// or in textual repr: setOwner(address)
+ function setOwner(address newOwner) external;
+
+ /// Changes collection owner to another substrate account
+ ///
+ /// @dev Owner can be changed only by current owner
+ /// @param newOwner new owner substrate account
+ /// @dev EVM selector for this function is: 0xb212138f,
+ /// or in textual repr: setOwnerSubstrate(uint256)
+ function setOwnerSubstrate(uint256 newOwner) external;
}
-/// @dev the ERC-165 identifier for this interface is 0x79cc6790
+/// @dev the ERC-165 identifier for this interface is 0x63034ac5
interface ERC20UniqueExtensions is Dummy, ERC165 {
+ /// Burn tokens from account
+ /// @dev Function that burns an `amount` of the tokens of a given account,
+ /// deducting from the sender's allowance for said account.
+ /// @param from The account whose tokens will be burnt.
+ /// @param amount The amount that will be burnt.
/// @dev EVM selector for this function is: 0x79cc6790,
/// or in textual repr: burnFrom(address,uint256)
function burnFrom(address from, uint256 amount) external returns (bool);
+
+ /// Mint tokens for multiple accounts.
+ /// @param amounts array of pairs of account address and amount
+ /// @dev EVM selector for this function is: 0x1acf2d55,
+ /// or in textual repr: mintBulk((address,uint256)[])
+ function mintBulk(Tuple6[] memory amounts) external returns (bool);
+}
+
+/// @dev anonymous struct
+struct Tuple6 {
+ address field_0;
+ uint256 field_1;
+}
+
+/// @dev the ERC-165 identifier for this interface is 0x40c10f19
+interface ERC20Mintable is Dummy, ERC165 {
+ /// Mint tokens for `to` account.
+ /// @param to account that will receive minted tokens
+ /// @param amount amount of tokens to mint
+ /// @dev EVM selector for this function is: 0x40c10f19,
+ /// or in textual repr: mint(address,uint256)
+ function mint(address to, uint256 amount) external returns (bool);
}
/// @dev inlined interface
tests/src/eth/base.test.tsdiffbeforeafterboth--- a/tests/src/eth/base.test.ts
+++ b/tests/src/eth/base.test.ts
@@ -94,7 +94,7 @@
});
itWeb3('ERC721 support', async ({web3}) => {
- expect(await contract(web3).methods.supportsInterface('0x58800161').call()).to.be.true;
+ expect(await contract(web3).methods.supportsInterface('0x780e9d63').call()).to.be.true;
});
itWeb3('ERC721Metadata support', async ({web3}) => {
tests/src/eth/fungibleAbi.jsondiffbeforeafterboth--- a/tests/src/eth/fungibleAbi.json
+++ b/tests/src/eth/fungibleAbi.json
@@ -151,33 +151,6 @@
"type": "function"
},
{
- "inputs": [
- { "internalType": "address", "name": "to", "type": "address" },
- { "internalType": "uint256", "name": "amount", "type": "uint256" }
- ],
- "name": "mint",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
- "inputs": [
- {
- "components": [
- { "internalType": "address", "name": "field_0", "type": "address" },
- { "internalType": "uint256", "name": "field_1", "type": "uint256" }
- ],
- "internalType": "struct Tuple0[]",
- "name": "amounts",
- "type": "tuple[]"
- }
- ],
- "name": "mintBulk",
- "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
- "stateMutability": "nonpayable",
- "type": "function"
- },
- {
"inputs": [],
"name": "getCollectionSponsor",
"outputs": [
@@ -220,6 +193,33 @@
"type": "function"
},
{
+ "inputs": [
+ { "internalType": "address", "name": "to", "type": "address" },
+ { "internalType": "uint256", "name": "amount", "type": "uint256" }
+ ],
+ "name": "mint",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
+ "inputs": [
+ {
+ "components": [
+ { "internalType": "address", "name": "field_0", "type": "address" },
+ { "internalType": "uint256", "name": "field_1", "type": "uint256" }
+ ],
+ "internalType": "struct Tuple6[]",
+ "name": "amounts",
+ "type": "tuple[]"
+ }
+ ],
+ "name": "mintBulk",
+ "outputs": [{ "internalType": "bool", "name": "", "type": "bool" }],
+ "stateMutability": "nonpayable",
+ "type": "function"
+ },
+ {
"inputs": [],
"name": "name",
"outputs": [{ "internalType": "string", "name": "", "type": "string" }],